Проблема с биндингом ListBox к Obs.Collection C# WPF MVVM

168
10 сентября 2021, 15:40

Существует ListBox ThreadBox.

ThreadBox:

<ListBox x:Name="ThreadBox" SelectionChanged="ThreadBox_SelectionChanged" Grid.Column="1" ItemsSource="{Binding MThreadPV}" >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="2">
                                <CheckBox IsChecked="{Binding Path=Check}"></CheckBox>
                                <TextBlock FontSize="16" HorizontalAlignment="Center">
                                    <Hyperlink TextDecorations="" NavigateUri="{Binding Path=Url}" Foreground="White" RequestNavigate="Hyperlink_RequestNavigate"><TextBlock FontSize="16" Text="{Binding Path=Thread}" HorizontalAlignment="Center"/></Hyperlink>
                                </TextBlock>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
</ListBox>

DataTemplate привязан к классу CheckedItem

CheckedItem:

public class CheckedItem
{
    private string thread;
    private string url;
    private bool check;
    private int id;
    public int ID
    {
        get { return id; }
        set { id = value; }
    }
    public bool Check
    {
        get { return check; }
        set { check = value; }
    }
    public string Thread
    {
        get { return thread; }
        set
        {
            thread = value;
        }
    }
    public string Url
    {
        get { return url; }
        set
        {
            url = value;
        }
    }
}

Пытаюсь приплести к ThreadBox ObservableCollection через MVVM.

Все делал по гайду https://habr.com/ru/post/338518/ , но по нулям.

Реализация модели:

class ThreadBoxModel : BindableBase
{
    private readonly ObservableCollection<source.CheckedItem> _myValues = new ObservableCollection<source.CheckedItem>();
    public readonly ReadOnlyObservableCollection<source.CheckedItem> MThreadValues;
    public ThreadBoxModel()
    {
        MThreadValues = new ReadOnlyObservableCollection<source.CheckedItem>(_myValues);
    }
    public void AddValue(source.CheckedItem item)
    {
        _myValues.Add(item);
        RaisePropertyChanged("T_Sum");
    }
    //проверка на валидность, удаление из коллекции и уведомление об изменении суммы
    public void RemoveValue(int index)
    {
        //проверка на валидность удаления из коллекции - обязанность модели
        if (index >= 0 && index < _myValues.Count) _myValues.RemoveAt(index);
        RaisePropertyChanged("T_Sum");
    }
    public int T_Sum => MThreadValues.Count; //сумма
}

Реализация VM:

public class MyThreadsVM : BindableBase
{
    readonly source.models.ThreadBoxModel _model = new source.models.ThreadBoxModel();
    public MyThreadsVM()
    {
        //таким нехитрым способом мы пробрасываем изменившиеся свойства модели во View
        _model.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };
        AddCommand = new DelegateCommand<source.CheckedItem>(str => {
            _model.AddValue(str);
        });
        RemoveCommand = new DelegateCommand<int?>(i => {
            if (i.HasValue) _model.RemoveValue(i.Value);
        });
    }
    public DelegateCommand<source.CheckedItem> AddCommand { get; }
    public DelegateCommand<int?> RemoveCommand { get; }
    public int T_Sum => _model.T_Sum;
    public ReadOnlyObservableCollection<source.CheckedItem> MThreadPV => _model.MThreadValues;
}

Использую ModernWindow поэтому:

<mui:ModernWindow.DataContext>
    <local:MyThreadsVM/>
</mui:ModernWindow.DataContext>

Элементы добавляю через (Сори за быдлокод):

private void ThreadBox_Update()
    {
        if (source.StaticData.cookies != null)
        {
            List<List<Dictionary<string, string>>> data = this.dark.GetMyThreads();
            int iterator = 0;
            source.models.ThreadBoxModel model = new source.models.ThreadBoxModel();
            foreach (var list in data)
            {
                string url = list[0]["url"];
                model.AddValue(new source.CheckedItem { Thread = list[0]["thread"], Url = $"{source.StaticData.head_url}{url}", ID = iterator, Check = false });
                iterator++;
            }
        }
    }

ThreadBox не обновляется. В чем может быть проблема?

Answer 1

В C# классы являются объектами и когда вы пишете new создается новый объект. Если вы делаете привязку к объекту, то он не должен создаваться по новой, ибо WPF будет по прежнему ссылаться на тот, ранее созданный экземпляр объекта.

У вас в коде, привязанная коллекция инициализируется в конструкторе ThreadBoxModel:

MThreadValues = new ReadOnlyObservableCollection<source.CheckedItem>(_myValues);

А саму модель ThreadBoxModel вы инициализируете несколько раз:

  1. В MyThreadsVM:

    readonly source.models.ThreadBoxModel _model = new source.models.ThreadBoxModel();
    
  2. В методе ThreadBox_Update():

    source.models.ThreadBoxModel model = new source.models.ThreadBoxModel();
    

В итоге вы получаете, что ваш интерфейс привязался к одной модели, а ваши данные добавились в совершенно другую модель.

Если вы хотите, что бы интерфейс знал, что ваша коллекция (или модель) переинициализировалась, то тут стоит его оповестить об этом. Для этого в WPF есть интерфейс INotifyPropertyChanged, вам просто надо завести свойство, где в set вы будете вызывать метод обновления от INPC.

READ ALSO
Gravity Scale 2D

Gravity Scale 2D

В компоненте Rigidbody2D есть такой параметр как Gravity Scale каким образом он влияет на глобальный компонент гравитации? Те

141
Unity. Использование кнопки &quot;Назад&quot; для вызова метода

Unity. Использование кнопки "Назад" для вызова метода

Можно ли обойтись без методов типа Update что бы заставить кнопку "назад" вызывать какой либо метод? Хотелось бы обойтись без постоянного вызова...

109
Десятичные числа MS ACCESS C#

Десятичные числа MS ACCESS C#

Суть в том, что у меня в базе данных есть как даты, так и десятичные числаОднако, при записывании в запрос числа с запятой он крашится с данным...

418
Перегрузка оператора c#

Перегрузка оператора c#

Есть класс Stack(стек реализованный на основе массива)(поля: массив stackArr и счётчик count)В нём реализован метод Pop(извлечение элемента из стека)

142