С Новым годом! Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.76/34: Рейтинг темы: голосов - 34, средняя оценка - 4.76
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
1
WPF

Как правильно реализовать связь между коллекциями Model и ViewModel?

27.10.2018, 14:38. Показов 6112. Ответов 17
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Приветствую, разбираюсь в паттерне MVVM, подскажите, как правильно реализовать связь между коллекциями Model и ViewModel
Допустим, в Model у меня есть коллекция, которая ею и пополняется (при подключении клиентов)
C#
1
public List<Client> ClientList { get; set; }
Как мне реализовать аналогичную коллекцию во ViewModel? (на нее сделана привязка из View)
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
27.10.2018, 14:38
Ответы с готовыми решениями:

Как правильно изменять свойства Model из ViewModel?
Есть модель с 10 свойствами. Есть VM так же с 10 свойствами, дублирующими модель. Есть (некое)...

Как мне правильно реализовать связь между таблицами
Имеются такая вот схема данных, точнее ее часть (смотрите во вложениях) Не устраивает меня связь...

Как создать связь между двумя ViewModel
Необходимо связать 2 списка из двух разных моделей. По отдельности привязываю некоторые компоненты...

Как передавать информацию из Model в ViewModel во время работы программы
Здравствуйте! Была у меня написана программка консольная для обработки файлов, ну и решил я ее...

17
1517 / 908 / 328
Регистрация: 17.05.2015
Сообщений: 3,433
27.10.2018, 18:38 2
Snaffu, никак. VM содержит в себе все коллекции. А заполняются они из классов модели.
1
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
27.10.2018, 22:53 3
Цитата Сообщение от Snaffu Посмотреть сообщение
Приветствую, разбираюсь в паттерне MVVM, подскажите, как правильно реализовать связь между коллекциями Model и ViewModel
Допустим, в Model у меня есть коллекция, которая ею и пополняется (при подключении клиентов)
C#Выделить код
1
public List<Client> ClientList { get; set; }
Как мне реализовать аналогичную коллекцию во ViewModel? (на нее сделана привязка из View)
Свойство коллекция, как уже отметил Рядовой, не типично для модели. Обычно в модели бывают методы возвращающие данные.
В целом по вопросу, делаете в ViewModel свойство для биндинга (обязательно с реализацией INotifyPropertyChanged). И устанавливаете у этого свойства ссылку на коллекцию, если не надо ни какого преобразования. Если надо - преобразуете элементы коллекции, фильтруете, сортируете и т.п. и результат присваиваете созданному свойству.
1
Wanna be serious
587 / 474 / 186
Регистрация: 31.07.2013
Сообщений: 1,693
27.10.2018, 23:32 4
Цитата Сообщение от Элд Хасп Посмотреть сообщение
обязательно с реализацией INotifyPropertyChanged
Не обязательно, например ObservableCollection<T> уже реализует INPC в своей реализации.
1
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 02:17 5
Цитата Сообщение от Bespridelschic Посмотреть сообщение
Не обязательно, например ObservableCollection<T> уже реализует INPC в своей реализации.
ObservableCollection уведомляет только об добавлении-удалении элементов. Больше не о чём другом не уведомляет. Что бы следить за изменением значений членов нужна реализация INPC для элементов. Так же нужна реализация INPC для свойства которое служит источником для привязки. Так как если изменится коллекция (а не её члены), то уведомления об этом не будет.

Очень часто используется сценарий - VM получает из Model список и после, на основании этого списка, присваивает свойству для привязки источника новую ссылку.

Добавлено через 8 минут
Посмотрите эти темы
Создание простого Binding'a (привязка класса к ListBox)
Перенаправление связки
1
Wanna be serious
587 / 474 / 186
Регистрация: 31.07.2013
Сообщений: 1,693
28.10.2018, 12:20 6
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Очень часто используется сценарий - VM получает из Model список и после, на основании этого списка, присваивает свойству для привязки источника новую ссылку.
Очень часто выгрузка подобных объектов в список потребляет через чур много ресурсов, поэтому обычно следят за изменениями и подгружают новые данные убирая не актуальные. Или же используют связку Clear/Add.

В любом случае, если задача действительно подразумевает проброс данных из Model во ViewModel, то соглашусь с Вами, необходимо использование INPC в отношении списка во ViewModel.
1
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 12:22 7
Цитата Сообщение от Bespridelschic Посмотреть сообщение
Очень часто выгрузка подобных объектов в список потребляет через чур много ресурсов, поэтому обычно следят за изменениями и подгружают новые данные убирая не актуальные. Или же используют связку Clear/Add.
Да, так и есть. Но в маленьких (особенно учебных) зачастую перебрасывают напрямую.
1
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
28.10.2018, 16:48  [ТС] 8
Цитата Сообщение от Элд Хасп Посмотреть сообщение
устанавливаете у этого свойства ссылку на коллекцию
Не подскажете, как это сделать?
0
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 16:56 9
Цитата Сообщение от Snaffu Посмотреть сообщение
Не подскажете, как это сделать?
Для этого хоть примерно надо знать приложение.
Для начала создайте класс OnPropertyChangedClass
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    public class OnPropertyChangedClass : INotifyPropertyChanged
    {
        #region Событие PropertyChanged и метод для его вызова
        /// <summary>Событие для извещения об изменения свойства</summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>Метод для вызова события извещения об изменении свойства</summary>
        /// <param name="prop">Изменившееся свойство</param>
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            string[] names = prop.Split("\\/\r \n()\"\'-".ToArray(), StringSplitOptions.RemoveEmptyEntries);
            switch  (names.Length)
            {
                case 0: break;
                case 1:
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
                    break;
                default:
                    OnPropertyChanged(names);
                    break;
            }
            
        }
 
        /// <summary>Метод для вызова события извещения об изменении списка свойств</summary>
        /// <param name="propList">Список имён свойств</param>
        public void OnPropertyChanged(IEnumerable<string> propList)
        {
            foreach (string prop in propList.Where(name => !string.IsNullOrWhiteSpace(name)))
                OnPropertyChanged(prop);
        }
 
        /// <summary>Метод для вызова события извещения об изменении списка свойств</summary>
        /// <param name="propList">Список свойств</param>
        public void OnPropertyChanged(IEnumerable<PropertyInfo > propList)
        {
            foreach (PropertyInfo prop in propList)
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop.Name));
        }
 
        /// <summary>Метод для вызова события извещения об изменении всех свойств</summary>
        /// <param name="propList">Список свойств</param>
        public void OnAllPropertyChanged() 
            => OnPropertyChanged(GetType().GetProperties());
        #endregion
    }
И ViewModel унаследуйте от него

Потом, что у Вас за Model. Какого объёма у неё данные? Как часто они меняются? Если событие извещающее об изменении данных?
Что за View у Вас? Какие используются источники, биндинги?
2
Wanna be serious
587 / 474 / 186
Регистрация: 31.07.2013
Сообщений: 1,693
28.10.2018, 17:16 10
Слишком громоздко для изучения. Можете использовать пример по проще:
C#
1
2
3
4
5
6
7
8
9
10
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
 
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ViewModel : BaseViewModel
{
    private List<Client> clientList;
    public List<Client> ClientList
    {
        get => clientList;
        set
        {
            clientList = value;
            OnPropertyChanged();
        }
    }
 
    public class ViewModel()
    {
        Model model = new Model();
        ClientList = model.GetCollection();
    }
}
1
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
28.10.2018, 17:23  [ТС] 11
Цитата Сообщение от Элд Хасп Посмотреть сообщение
что у Вас за Model. Какого объёма у неё данные? Как часто они меняются? Если событие извещающее об изменении данных?
Model - это TCP сервер, к которому могут подключаться клиенты, слать сообщения, файлы и т.д. Т.е. там есть список клиентов (онлайн) и файлов. Список обновляется при подключении\отключение (т.к. это для себя пишу, то не особо часто), никаких событий для ивещения нет.
Пока что для тренировки я бы хотел привязать список клиентов к ListBox'у на форме, поэтому забиндил ItemSource на ClientList из ViewModel
XML
1
2
3
4
5
6
7
8
9
10
<ListBox Style="{StaticResource MaterialDesignListBox}" ItemsSource="{Binding ClientList}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <materialDesign:PackIcon Kind="People" Width="17" Height="17" VerticalAlignment="Center" Margin="5"/>
                            <TextBlock Text="{Binding Name}" FontSize="17" VerticalAlignment="Center" Margin="5 0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
На сервере есть ClientList
C#
1
public List<Client> ClientList { get; set; }
Также есть класс Client
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client
    {
        public TcpClient TcpClient { get; set; }
        public NetworkStream Stream { get; set; }
        public string Name { get; set; }
        public CancellationTokenSource ClientToken { get; set; }
 
        public Client(TcpClient tcpClient, NetworkStream stream, string name, CancellationTokenSource token)
        {
            TcpClient = tcpClient;
            Stream = stream;
            //Stream.ReadTimeout = 1000;
            Name = name;
            ClientToken = token;
        }
    }
Хочу как-то связать ClientList из ViewModel с ClientList модели
0
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 17:39 12
С учётом того, что Вы сами будете следить за обновлением данных. Допустим будет кнопка нажатие на которую вызывает новый запрос к модели.
ViewModel унаследуйте или от BaseViewModel из поста Bespridelschic, или от класса OnPropertyChangedClass из моего поста. В моём классе прописаны дополнительные методы для вызова события списком свойств.
Далее в VM делаете свойство
C#
1
2
private List<Client> clientList;
public List<Client> ClientList { get=>clientList; set{clientList = value; OnPropertyChanged();}}
Так же в VM делаете метод который будет запрашивать данные в модели
C#
1
2
3
4
void GetClientList ()
{
     ClientList=model.ClientList;
}
Это с учётом того, что в model ссылка на модель. И ListBox сможет у Вас отобразить объекты класса Client.

Я часто делаю дополнительные классы уровня View, в которых только необходимая для View информация.

Допустим, один из типичных моих классов
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    public class ListBoxContentClass
    {
        /// <summary>Сортируемое значение</summary>
        public string DisplayOrder { get; set; }
        /// <summary>Индентификатор</summary>
        public string ID { get; set; }
        /// <summary>Отображаемое значение</summary>
        public string Display { get; set; }
        /// <summary>Для дополнительного выделения</summary>
        public bool IsHighlighted { get; set; } = false;
 
        /// <summary>Безпараметрический конструктор</summary>
        public ListBoxContentClass() { }
        /// <summary>Конструктор с установкой всех свойств</summary>
        public ListBoxContentClass(string DisplayOrder, string ID, string Display)
        {
            this.DisplayOrder = DisplayOrder;
            this.ID = ID;
            this.Display = Display;
        }
 
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            ListBoxContentClass m = obj as ListBoxContentClass; // возвращает null если объект нельзя привести к типу ListBoxContentClass
            if (m as ListBoxContentClass == null)
                return false;
 
            return m.ID == this.ID;
        }
 
        public override int GetHashCode()
        {
            var hashCode = 1356855956;
            hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(DisplayOrder);
            hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Display);
            return hashCode;
        }
    }
Это класс я использую для строчных значений в ListBox. И когда запрашиваю данные из модели преобразую их в этот класс.
2
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
28.10.2018, 19:54  [ТС] 13
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Так же в VM делаете метод который будет запрашивать данные в модели
Спасибо, так получилось. Только вот, допустим, что мне нужно обновление списка автоматически при подключении\отключении, а не по запросу, как это реализовать? Сейчас просто повесил на кнопку. Как вариант Task, который постоянно обращается к модели, но мне кажется, что это как-то "не очень".
0
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 20:37 14
Цитата Сообщение от Snaffu Посмотреть сообщение
Только вот, допустим, что мне нужно обновление списка автоматически при подключении\отключении,
Ну, у Вас же есть какой-то код осуществляющий подключении\отключении - поместите туда вызов метода запроса данных. Всё просто, как две копейки!
0
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
28.10.2018, 20:40  [ТС] 15
Цитата Сообщение от Элд Хасп Посмотреть сообщение
поместите туда вызов метода запроса данных
Т.е. модель будет вызывать метод из модели представления, который запросит у этой же модели список
0
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 21:07 16
Цитата Сообщение от Snaffu Посмотреть сообщение
Т.е. модель будет вызывать метод из модели представления, который запросит у этой же модели список
Нет! Я не правильно понял где, что у Вас расположено.

Добавлено через 2 минуты
Объясните ещё раз, что в каком порядке у Вас вызывается.
При запуске приложения - первой запускается View?
Как она потом связывается с VM и моделью?
0
7 / 6 / 7
Регистрация: 21.08.2016
Сообщений: 32
28.10.2018, 21:14  [ТС] 17
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Объясните ещё раз, что в каком порядке у Вас вызывается.
У меня есть MainView, где есть ListBox, в котором я хочу хранить клиентов. У этого ListBox есть свойство ItemSource, куда я привязываю ClientList из MainViewVM. Также есть модель Server, где также есть ClientList - список подключенных клиентов, и есть метод, который отвечает за принятие входного соединения и добавления клиента в список (также есть удаление клиента при отключении). Мне надо организовать связь между ClientList из MainViewVM и ClientList из Server.
У меня нет связи между представлением и моделью
Внутри модели представления создается объект модели Server (следуя подсказкам выше)
p.s. Извините, если ввел в заблуждение своим описанием)
0
Модератор
Эксперт .NET
15856 / 11002 / 2855
Регистрация: 21.04.2018
Сообщений: 32,328
Записей в блоге: 2
28.10.2018, 21:21 18
Цитата Сообщение от Snaffu Посмотреть сообщение
У меня нет связи между представлением и моделью
Внутри модели представления создается объект модели Server (следуя подсказкам выше)
Прямой связи между View и Model и не должно быть.
Теперь что я понял из Ваших объяснений.
Первой вызывается View. View создаёт VM. VM - модель. При создании модели, автоматически создаётся соединение.
Если так, то после создания модели, VM надо вызвать метод GetClientList().
Ещё такой нюанс. Если модель у Вас сервер, то каким образом создаётся соединение и получаются данные: синхронно или асинхронно? Если асинхронно, то Вам не обойтись без создания события в модели извещающем о готовности данных.
1
28.10.2018, 21:21
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
28.10.2018, 21:21
Помогаю со студенческими работами здесь

Как реализовать связь между ячейкой и GroupBox1
Добрый день. Прошу у вас помощи! Можно ли реализовать связь между DBGrit и GroupBox`ми. На...

Model или ViewModel?
Скажите пожалуйста если класс не используется в качестве DataContext-а ни в одном представлении, но...

UML и javascript, как правильно показать связь между объектами
Изучаю ООП и собственно сам UML Работаю с javascript. Сначала загружается Jquery,...

Связь между таблицами(Главный-Подчиненный). Как правильно связать?
Добрый день. По какому принципу выбирается главная-таблица и подчиненная таблица? Например,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Блоги программистов
Как перейти с Options API на Composition API в Vue.js
BasicMan 06.01.2025
Почему переход на Composition API актуален В мире современной веб-разработки фреймворк Vue. js продолжает эволюционировать, предлагая разработчикам все более совершенные инструменты для создания. . .
Архитектура современных процессоров
inter-admin 06.01.2025
Процессор (центральный процессор, ЦП) является основным вычислительным устройством компьютера, которое выполняет обработку данных и управляет работой всех остальных компонентов системы. Архитектура. . .
История создания реляционной модели баз данных, правила Кодда
Programming 06.01.2025
Предпосылки создания реляционной модели В конце 1960-х годов компьютерная индустрия столкнулась с серьезными проблемами в области управления данными. Существовавшие на тот момент модели данных -. . .
Полезные поделки на Arduino, которые можно сделать самому
raxper 06.01.2025
Arduino как платформа для творчества Arduino представляет собой удивительную платформу для технического творчества, которая открывает безграничные возможности для создания уникальных проектов. Эта. . .
Подборка решений задач на Python
IT_Exp 06.01.2025
Целью данной подборки является предоставление возможности ознакомиться с различными задачами и их решениями на Python, что может быть полезно как для начинающих, так и для опытных программистов. . . .
С чего начать программировать микроконтроллер­­ы
raxper 06.01.2025
Введение в мир микроконтроллеров Микроконтроллеры стали неотъемлемой частью современного мира, окружая нас повсюду: от простых бытовых приборов до сложных промышленных систем. Эти маленькие. . .
Из чего собрать игровой компьютер
inter-admin 06.01.2025
Сборка игрового компьютера требует особого внимания к выбору комплектующих и их совместимости. Правильно собранный игровой ПК не только обеспечивает комфортный геймплей в современных играх, но и. . .
Обновление сайта www.historian.b­y
Reglage 05.01.2025
Обещал подвести итоги 2024 года для сайта. Однако начну с того, что изменилось за неделю. Добавил краткий урок по последовательности действий при анализе вредоносных файлов и значительно улучшил урок. . .
Как использовать GraphQL в C# с HotChocolate
Programming 05.01.2025
GraphQL — это современный подход к разработке API, который позволяет клиентам запрашивать только те данные, которые им необходимы. Это делает взаимодействие с API более гибким и эффективным по. . .
Модель полного двоичного сумматора с помощью логических операций (python)
AlexSky-coder 04.01.2025
def binSum(x:list, y:list): s=^y] p=x and y for i in range(1,len(x)): s. append((x^y)^p) p=(x and y)or(p and (x or y)) return s x=list() y=list()
Это мы не проходили, это нам не задавали...(аси­­­­­­­­­­­­­­хро­н­н­ы­й счётчик с управляющим сигналом задержки).
Hrethgir 04.01.2025
Асинхронный счётчик на сумматорах (шестиразрядный по числу диодов на плате, но наверное разрядов будет больше - восемь или шестнадцать, а диоды на старшие), так как триггеры прошли тестирование и. . .
Руководство по созданию бота для Телеграм на Python
IT_Exp 04.01.2025
Боты для Телеграм представляют собой автоматизированные программы, которые выполняют различные задачи, взаимодействуя с пользователями через интерфейс мессенджера. В данной статье мы рассмотрим,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru