Уведомление View об изменении ObservableCollection.Count

236
19 мая 2018, 13:50

При добавлении\удалении элемента в коллекции, View не отображает изменения количества элементов. Так же как и при изменении статуса в вложенном объекте.

public class GroupComputersVM : BaseVM
{
    public GroupComputersVM(string name)
    {
        Name = name;
    }
    private string _Name;
    public string Name { get { return _Name; } set { _Name = value; OnPropertyChanged("Name"); } }
    public string Online { get { return ComputersList.Where(x => x.Status).Count().ToString() + @"/" + ComputersList.Count.ToString(); } }
    private ObservableCollection<ComputerVM> _ComputersList = new ObservableCollection<ComputerVM>();
    public ObservableCollection<ComputerVM> ComputersList { get { return _ComputersList; } set { _ComputersList = value; } }
}
public class ComputerVM : BaseVM
{
    private string _Name;
    public string Name { get { return _Name; } set { _Name = value; OnPropertyChanged("Name"); } }
    private bool _Status;
    public bool Status { get { return _Status; } private set { _Status = value; OnPropertyChanged("Status"); } }
}

MainWindowViewModel:

public class MainWindowViewModels : ViewModelBase
{
    public MainWindowViewModels(LogicVM logicVM)
    {
        GroupList = logicVM.GroupList; // поле: public ObservableCollection<GroupComputersVM> GroupList
    }
    public ObservableCollection<GroupComputersVM> GroupList { get; set; }
}

View:

 <ListBox ItemsSource="{Binding GroupList}" HorizontalContentAlignment="Stretch">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid Height="40">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="140*"/>
                            <ColumnDefinition Width="30*"/>
                        </Grid.ColumnDefinitions>
                        <Label Content="{Binding Name}" Padding="3,0,0,0" VerticalContentAlignment="Center"/>
                        <Label Content="{Binding Online}" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Column="1"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

UPD

Answer 1

Подготовка

Посмотрев на ваш скриншот я понял, что вам нужно просто вывести группы и кол-во объектов. Ок, давайте разберемся сколько уровней у нас будет:

  • Сам список у нас должен содержать группы.
    • "внутренности" группы будут следующими:
      • имя
      • кол-во онлайн (обновляемое)
      • коллекция самих элементов, которая содержит внутренности такого вида:
        • имя
        • статус (обновляемое)

Я сделал пометки "(обновляемое)" у тех свойств, которым надо реализовать INPC. Ну что, давайте приступим к реализации!

INotifyPropertyChanged

Что это и как использовать?
Ну для начала представим, что на нашей View есть некое текстовое поле в которое мы вводим текст. Как поведет себя программа, если у нас сделана простая привязка к некому свойству Text? В основном из кода вы увидите изменения свойства, но что если наоборот, нам надо изменить значение в коде и что бы оно изменилось в нашей View? Вот в таком случае наш интерфейс не будет знать о том, что свойство было изменено. Для оповещений собственно и используется INPC. Есть как минимум 2 коллекции, которые уже реализуют подобные интерфейсы - это ObservableCollection и BindingList. Для них нет необходимости городить сотню INPC (как у вас).

Хорошо, давайте сделаем базовый класс, который в последующим будем использовать:

public class VM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ViewModel

ViewModel - это некая прослойка между Model и View в которой реализуется логика для определенного элемента. В нашем случае нужно создать 3 VM (item, group и main).

  • ItemViewModel - как мы уже определили, в ней должно находится 2 свойства, это имя и статус. Статус у нас может обновится из кода, по этому надо реализовать INPC (наследуемся от ранее созданного класса). Код у нас будет примерно таким:

    public class ItemViewModel : VM
    {
        public string Name { get; set; }
        private bool status;
        public bool Status
        {
            get => status;
            set
            {
                status = value;
                OnPropertyChanged();
            }
        }
    }
  • GroupViewModel - тут также потребуется имя, кол-во онлайн и коллекция. Счетчик онлайн у нас обновляется (реализуем INPC). Что касается коллекции, то нам надо отслеживать статус элементов. Для этого я буду использовать BindingList и подпишусь на ListChanged событие, которое будет вызывать обновление счетчика онлайн. Код в итоге будет примерно следующим:

    public class GroupViewModel : VM
    {
        public GroupViewModel()
        {
            List.ListChanged += ListOnListChanged;
        }
        public string Name { get; set; }
        public BindingList<ItemViewModel> List { get; set; } = new BindingList<ItemViewModel>();
        private int online;
        public int Online
        {
            get => online;
            set
            {
                online = value;
                OnPropertyChanged();
            }
        }
        private void ListOnListChanged(object sender, ListChangedEventArgs e)
        {
            //if (e.ListChangedType == ListChangedType.ItemChanged)
            UpdateOnline();
        }
        private void UpdateOnline() => Online = List.Count(x => x.Status);
    }

Видите закомментированную строку? Так мы можем сделать ограничение, при котором будет обновлять статус (к примеру только при изменении, или только при добавление).

  • MainViewModel - Это уже наша основная VM, которая в свою очередь привязывается. В ней мы создаем основную коллекцию наших групп, ну и для теста заполним ее:

    public class MainViewModel
    {
        public MainViewModel()
        {
            Groups.Add(new GroupViewModel{Name = "Машины"});
            var machine = Groups.FirstOrDefault(x => x.Name == "Машины")?.List;
            machine?.Add(new ItemViewModel{Name = "Машина 1", Status = true});
            machine?.Add(new ItemViewModel{Name = "Машина 2", Status = true});
            machine?.Add(new ItemViewModel{Name = "Машина 3", Status = false});
        }
        public ObservableCollection<GroupViewModel> Groups { get; set; } = new ObservableCollection<GroupViewModel>();
    }

Супер, осталось только привязать это все дело, я это сделаю в MainWindow таким способом:

private MainViewModel MainViewModel { get; } = new MainViewModel();
public MainWindow()
{
    InitializeComponent();
    DataContext = MainViewModel;
}

XAML

В самом XAML я лично нечего мудрить не буду, сделаю просто вывод кол-ва элементов коллекции и саму коллекцию:

<StackPanel>
    <TextBlock Text="{Binding Groups.Count, StringFormat=Группы: {0}}"/>
    <ListBox ItemsSource="{Binding Groups}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" Margin="5,0"/>
                    <TextBlock>
                    <Run Text="{Binding Online, Mode=OneWay}"/>
                    <Run Text="/"/>
                    <Run Text="{Binding List.Count, Mode=OneWay}"/>
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

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

Сам результат:

READ ALSO
подключение OpenGL к GLFW на c#

подключение OpenGL к GLFW на c#

хотел использовать OpenGL и GLFWподключил https://github

196
Условия if else и switch

Условия if else и switch

Возможно ли такое сделать с Switch ?

201
Используя структуры и коллекции .NET Framework, реализовать [требует правки]

Используя структуры и коллекции .NET Framework, реализовать [требует правки]

1Получить от пользователя (путем ввода с консоли) множество строк и сформировать из них список (список 1)

175