Member 13668663 Ответов: 1

Привязки данных, похоже, не обновляются в MVVM


Я пытаюсь переписать свой CheckBoxList из кода позади в шаблон MVVM в WPF C#. Дело в том, что теперь у меня есть проблема, чтобы получить все выбранные флажки. Я реализовал интерфейс INotifyPropertyChanged. Проект строится правильно, когда я устанавливаю точку останова, я могу заметить, что всегда получал ложное значение для своих флажков, даже если они выбраны. Я предполагаю, что, возможно, я сделал что-то не так с привязкой данных. Пожалуйста, кто-нибудь может помочь? Я совершенно новичок в MVVM.

Что я уже пробовал:

 public class ObservableObject : INotifyPropertyChanged
    {

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string strPropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(strPropertyName));
        }

        #endregion

    }
}


public class SharedModel : ObservableObject
   {
               private bool _fIsSelected;

               public bool IsSelected
               {
                   get => _fIsSelected;
                   set
                   {
                       _fIsSelected = value;
                       OnPropertyChanged("IsSelected");
                   }
               }

       public bool IsSelected { get; set; }

       public string Name { get; set; }

       public string Method { get; set; }
   }



class TestViewModel : ObservableObject
    {

        public ObservableCollection<SharedModel> List { get; set; } = new ObservableCollection<SharedModel>
        {
            new SharedModel
            {
                Name = "A1",
                Method = Test(),
            },
            new SharedModel
            {
                Name = "A2",
                Method = TestOne()
            }
};

        public string GetSelectedCheckboxes()
        {
             var command =
                    from item in List
                    where item.IsSelected
                    select item.Method;
          return string.Join("\r&", new NewList<string>(command));
        }


<StackPanel Margin="0,0,769,510">
            <ListBox Name="ListBox"
                     ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                     ItemsSource="{Binding List}"
                     SelectionMode="Multiple" Background="{x:Null}" Margin="0,133,590,470" Foreground="White" BorderBrush="{x:Null}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"
                                    MinWidth="170" MaxWidth="170"
                                    Margin="0,0, 0, 0" >
                            <CheckBox x:Name="TestCheckbox"
                                      Tag="{Binding Method}" IsChecked="{Binding IsSelected}" />
                            <ContentPresenter
                                Content="{Binding Name}"
                                Margin="5,0, 15, 0" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>

George Swan

Свойство IsSelected должно вызывать OnPropertyChanged() в своем сеттере

1 Ответов

Рейтинг:
4

J. Calhoun

Во-первых, вы не реализуете событие OnChanged в своей модели, а только модель представления.
Далее, одна вещь, с которой вы должны быть осторожны в привязке данных, - это ключевое слово "new".

Практика, которая, как я обнаружил, работает лучше всего для меня, - это объявление переменной

privateObservableCollection<SharedModel> _List;
public ObservableCollection<SharedModel> List 
{ 
    get { return _List; } 
    set { _List = value; OnPropertyChanged("List");} 
}


Затем я инициализирую его в конструкторе так, чтобы он только один раз использовал ключевое слово "new". Причина этого заключается в том, что ключевое слово "new" часто "ломает" привязку. Я не уверен, что вы хотите изменить список из viewmodel, поэтому я обычно указываю двухсторонний режим, чтобы я мог изменять элементы как из view, так и из viewmodel. Я также изменяю триггер источника обновления, но по умолчанию используется IIRC lostfocus.

<Listview Itemssource="{Binding List, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 

Затем в вашем шаблоне элемента ваш IsSelected должен обновиться.
<ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"

                                    MinWidth="170" MaxWidth="170"

                                    Margin="0,0, 0, 0" >
                            <CheckBox x:Name="TestCheckbox"

                                      Tag="{Binding Method}" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                            <ContentPresenter

                                Content="{Binding Name}"

                                Margin="5,0, 15, 0" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>



Я не знаю, используете ли вы команду, событие или что-то еще, чтобы получить GetSelectedCheckboxes (), но есть миллион различных способов вернуть все истинные выборки. один из способов может быть:



public string GetSelectedCheckboxes()
        {
             List<string> yourlist = List.where(l => l.IsSelected == true).Select(x => x.Method).ToList();
             return yourlist;   
        }


Надеюсь, это поможет, если вам нужно, чтобы я уточнил дальше, я просто дам мне знать.


Правка: чтобы показать вам, как я его настроил.

public class SharedModel
   {
       public bool IsSelected { get; set; }

       public string Name { get; set; }

       public string Method { get; set; }
   }


public class TestViewModel : ObservableObject
{
    privateObservableCollection<SharedModel> _List;
    public ObservableCollection<SharedModel> List 
    { 
        get { return _List; } 
        set { _List = value; OnPropertyChanged("List");} 
    }

    public TestViewModel()
    {
        List = new ObservableCollection<SharedModel>()
        {
            new SharedModel()
            {
                Name = "A1",
                IsSelected = false
            },
            new SharedModel()
            {
                Name = "A2",
                IsSelected = false
            }
        };
    }

    public List<string> GetSelectedCheckboxes()
    {
        List<string> selectedCheckboxes = List.Where(x => x.IsSelected == true).Select(x => x.Name).ToList();
        return selectedCheckboxes;
}


Это вместе с настройкой DataContext должно работать. Все, что вам нужно сделать, это подключить кнопку для вызова GetSelectedCheckboxes (), и этот список будет состоять только из выбранных флажков.


Member 13668663

Спасибо за ваш ответ :)
Сейчас я меняю свой код. Я хотел бы спросить об этой части:

privateObservableCollection<sharedmodel> _List;
публичный список ObservableCollection<sharedmodel>
{
get { return _List; }
set { _List = value; OnPropertyChanged("список");}
}

Что мне нужно, так это точно уведомить, что флажок установлен, поэтому я попробовал следовать вашим предложениям, и теперь у меня есть что-то вроде этого:


класс TestViewModel : ObservableObject
{
public ObservableCollection<sharedmodel> List { get; set; }
общественного недействительными CreateCheckBoxList()
{
List = new ObservableCollection<sharedmodel>
{
новая модель SharedModel
{
Имя = "A1",
Метод = Тест(),
},
новая модель SharedModel
{
Имя = "A2",
Метод = Тестон()
}
};
}
публичная строка GetSelectedCheckboxes()
{
команда var =
из пункта в списке
где пункт.Изменили реализацию
выбранный пункт.Метод;
возвращенная строка.Join("\r&", new NewList<string>(команда));
}
....

Я также внес изменения в xaml, как вы и предлагали. Я не уверен, как я могу назвать этот список в пользовательском интерфейсе сейчас. Эта коллекция должна быть вызвана в конструкторе, например, из TestList.xaml.cs?

J. Calhoun

Я предположил, что вы установили datacontext, если видите, что ваш listview заполняется вашим itemssource? Если вы устанавливаете свой datacontext, то свойство должно обновиться, если вам нужен способ вызова GetSelectedCheckboxes (), вы всегда можете настроить кнопку или какой-то командный элемент управления для его вызова.

Обычно я никогда не использую файл xaml.cs, так как просто ссылаюсь на viewmodel из xaml.

xmlns:vm="clr-пространство имен:yourtestviewmodelproject"

Затем поместите его в datacontext

<окно.DataContext>
<vm:testviewmodel>
**здесь должен быть закрывающий тег datacontext, но когда я его набираю, он по какой-то причине не отображается**



или если вы используете xaml.cs, то в конструкторе вы просто установите
этот.DataContext = новый TestViewModel();

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

Если вы делаете эти вещи, и это все еще не работает, дайте мне знать, и я могу сделать быстрый образец проекта и показать вам.

Member 13668663

Проблема в том, что я не могу видеть список в представлении сейчас

Вот как я настраиваю DataContext

публичный TestListView()
{
метод InitializeComponent();
viewModel = новый TestViewModel();
этот.Класс DataContext = модель представления;
}

J. Calhoun

В TestViewModel вы это делаете

публичная модель TestViewModel()
{
CreateCheckBoxList()
}

J. Calhoun

При обновлении моего решения я заметил, что вам не нужно расширять ObservableObject из вашей модели. И я поместил загрузку списка в конструктор, но вместо этого вы можете использовать свой CreateCheckboxList (). В любом случае это не имеет значения. С набором datacontext все это должно работать. Я очень быстро собрал тестовый проект, и все это прекрасно работает.

Member 13668663

Спасибо, теперь я все понимаю. Я удалил и код сзади.
У меня есть последний вопрос, не могли бы вы предоставить какой-нибудь образец, который вы упомянули выше. У меня все еще есть проблема привязки данных. Я пытаюсь вызвать GetSelectedTests() под кнопкой, но вижу в отладчике, что bolean ложен, даже если установлен флажок.
Тогда я должен потратить больше времени, чтобы копаться, я вижу, что я пропустил.

J. Calhoun

https://github.com/JACWagon/SampleMVVMExample

Я разместил здесь публичный репозиторий, где вы можете скачать этот пример. Если у вас возникнут проблемы с его получением, я могу отправить вам проект по электронной почте.

В основном я показываю ту же структуру, что и вы, есть несколько незначительных отличий, но я также включил проект Execute Path, который вы можете скопировать и пропустить, что поможет вам с командами, когда дело доходит до кнопок. Если вы хотите узнать больше о том, как это работает, Google ICommand-это мощный интерфейс, который вы можете использовать в MVVM. Я часто им пользуюсь. Кроме того, я с удовольствием отвечу на любые ваши вопросы об этом проекте.

Member 13668663

Большое вам спасибо, теперь все работает. У меня также были проблемы с ICommand. Я надеюсь, что в будущем смогу кому - то помочь-так же, как вы помогли мне сейчас.
Мне есть чему поучиться с помощью mvvm, но теперь я вижу, как эти компоненты взаимодействуют.

J. Calhoun

Не беспокойтесь! Рад, что смог помочь!