0

Существует 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 не обновляется. В чем может быть проблема?

icYFTL
  • 1,662
  • 2
    А проблема то в чем? – EvgeniyZ Oct 13 '19 at 14:06
  • А. Ну да. ListBox не обновляется. – icYFTL Oct 13 '19 at 15:25
  • В плане не обновляется? Не добавляются новые элементы или что? – EvgeniyZ Oct 13 '19 at 15:27
  • Да. обновил вопрос, добавил момент с добавлением элементов в коллекцию – icYFTL Oct 13 '19 at 15:30
  • А ThreadBox_Update() у вас постоянно вызывается? – EvgeniyZ Oct 13 '19 at 15:36
  • Нет, 1 раз вызывается. При старте программы. – icYFTL Oct 13 '19 at 15:38
  • Я тут грешу на следующее: Вы два раза инициализируете коллекцию (первый раз в конструкторе, а второй раз в ThreadBox_Update()). Когда вы делаете привязку, она привязывается только к одному экземпляру, если он пересоздается, то привязка не видит этого без INPC свойства и продолжает работать со старым объектом. – EvgeniyZ Oct 13 '19 at 17:25
  • Возможно. Но я пересел уже на другую реализацию через INotifyPropertyChanged. – icYFTL Oct 13 '19 at 17:34
  • Проблема заключается в том, что я не могу добавлять (не знаю почему) данные в коллекцию через метод ThreadBoxModel.AddValues(), а так все работает как и должно (наверное) – icYFTL Oct 13 '19 at 17:35
  • Кстати да. Похоже это из-за переинициализации коллекции. Спасибо за помощь, выкиньте в ответ, я лучшим отмечу. – icYFTL Oct 13 '19 at 17:38
  • Вы себе зачем то усложняете жизнь с этими ReadOnly коллекциями, научитесь просто пока использовать обычные коллекции и поймите как это все работает. Можете например посмотреть один из моих ответов, где я объяснял как и что в MVVM нужно. Кстати я вам также советую посмотреть этот ответ, он поможет вам не совершить ошибку в будущем с установкой DataContext. По поводу ответа, я напишу конечно, но где то был дубликат этого вопроса, если найду - надо будет закрыть на него. – EvgeniyZ Oct 13 '19 at 17:49

1 Answers1

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.

EvgeniyZ
  • 15,694