Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.53/47: Рейтинг темы: голосов - 47, средняя оценка - 4.53
 Аватар для imcavs™
-2 / 24 / 8
Регистрация: 19.02.2012
Сообщений: 446

WPF MVVM ComboBox Data Binding

03.08.2019, 20:10. Показов 9176. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем добрый вечер. Есть у кого в наличии уже готовая реализация подвязки комбобокса к данными из модели ?
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
03.08.2019, 20:10
Ответы с готовыми решениями:

Binding RichTextBox using MVVM WPF
Добрый вечер, друзья. А как у нас обстоят дела с биндингом ричтекстбокса на паттерне mvvm? По аналогии с текстбоксом, сделать...

WPF DataGrid Data binding
Возникла необходимость сделать биндинг для int в DataGrid с возможностью редактирования. <DataGrid...

WPF MVVM - Transfer Data to Model
Всем привет, уважаемые. Есть два этапа: 1. Переношу коллекцию ObservableCollection из VM в Model. 2. А затем из Model в другую VM. ...

14
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
03.08.2019, 22:31
Лучший ответ Сообщение было отмечено imcavs™ как решение

Решение

Цитата Сообщение от imcavs™ Посмотреть сообщение
Есть у кого в наличии уже готовая реализация подвязки комбобокса к данными из модели ?
Model
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public class RandomColorModel : OnPropertyChangedClass
    {
        private static readonly Random random = new Random();
        private static readonly string[] namesAllColors = typeof(Brushes)
            .GetProperties()
            .Select(prop => prop.Name)
            .ToArray();
        public ObservableCollection<string> RandomColors { get; } = new ObservableCollection<string>();
 
        public void GenerateRandomColors(uint count)
        {
            RandomColors.Clear();
            foreach (string name in namesAllColors.OrderBy(x => random.Next()).Take((int)count))
                RandomColors.Add(name);
        }
    }
View
XML
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
<Window x:Class="CyberForum.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CyberForum"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <local:RandomColorVM/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        <Grid VerticalAlignment="Center" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="Количество цветов: "/>
            <TextBox Grid.Column="1" Text="{Binding CountColors}" HorizontalContentAlignment="Center"/>
        </Grid>
        <Button Grid.Row="1" Content="Генерация нового набора" 
                Command="{Binding GenerateRandomColorsCommand}" 
                CommandParameter="{Binding CountColors}"/>
        <TextBlock Grid.Column="1" Text="Выберите цвет из набора" HorizontalAlignment="Center"/>
        <ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding RandomColors}" SelectedItem="{Binding SelectColor}"/>
        <Border Grid.Column="2" Grid.RowSpan="2"  Background="{Binding SelectColor}">
            <TextBlock Text="{Binding SelectColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Border>
    </Grid>
</Window>
ViewModel
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
    public class RandomColorVM : OnPropertyChangedClass
    {
        private uint _countColors;
        private string _selectColor;
        private RelayCommand _selectColorCommand;
        RandomColorModel model = new RandomColorModel();
 
        public IEnumerable<string> RandomColors => model.RandomColors;
        public uint CountColors { get => _countColors; set { _countColors = value; OnPropertyChanged(); } }
 
        public string SelectColor { get => _selectColor; set { _selectColor = value; OnPropertyChanged(); } }
        public RelayCommand GenerateRandomColorsCommand => _selectColorCommand ?? (_selectColorCommand = new RelayCommand(GenerateRandomColorsMetod, GenerateRandomColorsCanMetod));
 
        private bool GenerateRandomColorsCanMetod(object parameter)
            => uint.TryParse(parameter?.ToString(), out uint _tmp);
 
        private void GenerateRandomColorsMetod(object parameter)
        {
            if (GenerateRandomColorsCanMetod(parameter))
                model.GenerateRandomColors(uint.Parse(parameter.ToString()));
            SelectColor = null;
        }
 
        public RandomColorVM() => GenerateRandomColorsMetod(CountColors = 5);
    }
0
 Аватар для imcavs™
-2 / 24 / 8
Регистрация: 19.02.2012
Сообщений: 446
04.08.2019, 20:22  [ТС]
Элд Хасп, вы в примере используете ObservableCollection<string> в модели. А как подкорректировать, для использования ObservableCollection<от класса>?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
04.08.2019, 20:31
Цитата Сообщение от imcavs™ Посмотреть сообщение
А как подкорректировать, для использования ObservableCollection<от класса>?
Стринг - это тоже класс...!

А по делу - напишите, толком что вы хотите реализовать.
0
 Аватар для imcavs™
-2 / 24 / 8
Регистрация: 19.02.2012
Сообщений: 446
04.08.2019, 20:39  [ТС]
Элд Хасп, пожалуйста.
Вкратце, задача. В модели формируется список почтовых рассылок.

Model:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ObservableCollection<GroupMail> groupmaillist;
        public ObservableCollection<GroupMail> GetGroupMailList()
        {
            return groupmaillist;
        }
 
public async Task LoadGroupMailList()
        {
            await Task.Delay(1000);
            ///Далее начинаем загружать данные из БД и заносить в список
            groupmaillist = new ObservableCollection<GroupMail>()
            {
                new GroupMail { group_mail_id = 1, group_mail_header = "Отдел кадров", group_mail_description="Рассылка для отделов кадров"},
                new GroupMail { group_mail_id = 2, group_mail_header = "Отдел аналитики", group_mail_description = "Рассылка отдела аналитики" },
                new GroupMail { group_mail_id = 3, group_mail_header = "Отдел продаж", group_mail_description = "Рассылка отдела продаж" },
            };
            OnPropertyChanged("Список групп загружен успешно");
            Logger.Logbox += "Список групп загружен успешно";
        }
Далее на форме, идёт добавление почтового адреса в необходимую группу рассылки.
Вот этот список и подтягивается из модели.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
04.08.2019, 21:00
Цитата Сообщение от imcavs™ Посмотреть сообщение
В модели формируется список почтовых рассылок.
При использовании многопоточности ObservableCollection Модели нельзя напрямую биндить в View.

Где-то в темах я делал пример как надо обновлять коллекцию в многопоточном приложении.

Для вашей задачи коллекция должна создаваться в VM. По команде из View вызывается метод Модели с нужными параметрами. Модель асинхронно изменяет коллекцию.

Для PropertyChanged Модели создаётся производный от PropertyChangedEventArgs класс с дополнительным свойством типа object. В этом свойстве Модель в событии передаёт какие именно элементы были добавлены (удаленны) в коллекцию. И VM получив событие и данные в его параметрах меняет свою коллекцию уже в основном UI потоке.
0
 Аватар для imcavs™
-2 / 24 / 8
Регистрация: 19.02.2012
Сообщений: 446
05.08.2019, 09:08  [ТС]
Элд Хасп, я сделал таким образом.

Model:
C#
1
2
3
4
5
6
7
8
 public ObservableCollection<string> GetGroupIE()
        {
            foreach (var item in groupmaillist)
            {
                GroupIE.Add(item.group_mail_header);
            }
            return GroupIE;
        }
В модели метод получения списка для combox'а.
Всё работает, но в моём понимании это не самый лучший вариант.
Потому что модель создаёт специально для view свой список. Согласно паттерну mvvm этого не должно быть.
0
215 / 149 / 48
Регистрация: 28.12.2016
Сообщений: 716
05.08.2019, 09:33
imcavs™, ну она создает для выбора. Вы так же можете сюда подставить консоль, wf, а значит mvvm не нарушен))

К тому же она ничего не знает о View

View -> VM -> Model
View <- VM <- Model
0
 Аватар для imcavs™
-2 / 24 / 8
Регистрация: 19.02.2012
Сообщений: 446
05.08.2019, 09:37  [ТС]
Элд Хасп, вообщем, уже исправил ошибки.
Перенёс код создания списка IE для combobox'а во ViewModel:

C#
1
2
3
4
5
6
7
8
9
10
11
        
public IEnumerable<string> GroupIE => GetGroupIE();
public ObservableCollection<string> GetGroupIE()
        {
            var templist = new ObservableCollection<string>();
            foreach (var item in Model.groupmaillist)
            {
                templist.Add(item.group_mail_header);
            }
            return templist;
        }
Указанный вариант, мне оптимально подходит, я на мой взгляд нет нарушения шаблона mvvm.

Добавлено через 1 минуту
Defences, я сразу же представлю, что будет, если будет 100 форм со своими IE списками.
Вы бы как реализовали, в таком случае создание IE списков: все в одной модели, или каждый IE в своей VM ?
0
215 / 149 / 48
Регистрация: 28.12.2016
Сообщений: 716
05.08.2019, 09:58
imcavs™, ObservableCollection<string> в VM, я тянуть данные из модели.

Грубо говоря модель генерирует 5 чисел -> vm берет их и добавляет в обсервер -> view отображает по привязке -> button click -> vm берет индекс combobox -> отправляет число в model -> действия с этим числом.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
05.08.2019, 10:14
Цитата Сообщение от imcavs™ Посмотреть сообщение
Перенёс код создания списка IE для combobox'а во ViewModel:
Это правильно.
ObservableCollection - это коллекция которая нужна для View и в Модели её быть не должно.
Просто для простоты в однопоточных решениях с этим не заморачиваются - делают коллекцию в Модели.
Из модели лучше всего получать список в виде IEnumerable.

Вы в теме просили показать привязку CovboBox - готовый пример. Я скинул то, что когда-то делал по чьёй-то просьбе. На обновлении коллекции акцентов не было. У вас же многопоточное приложение и такое создание коллекции применять нельзя.

Я сейчас подправлю и скину.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
05.08.2019, 12:46
Лучший ответ Сообщение было отмечено imcavs™ как решение

Решение

imcavs™, вот демонстрационное многопоточное решение .

Модель.
Все методы изменения коллекции асинхронные.
Слипы стоят для эмуляции задержки.
Для извещения об изменении коллекции используется PropertyChanged с передачей дополнительных данных.

Класс PropertyChangedDataEventArgs для передачи дополнительных данных
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public class PropertyChangedDataEventArgs : PropertyChangedEventArgs
    {
        public object RemoveData { get; set; }
        public object AddData { get; set; }
 
        public PropertyChangedDataEventArgs(string propertyName)
            : base(propertyName) { }
        public PropertyChangedDataEventArgs(string propertyName, object removeData, object addData)
            : this(propertyName)
        {
            RemoveData = removeData;
            AddData = addData;
        }
    }
Базовый класс с реализацией INPC с возможностью передачи дополнительных данных
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    /// <summary>Базовый класс с реализацией INPC </summary>
    public abstract class OnPropertyChangedClass : INotifyPropertyChanged
    {
        /// <summary>Событие для извещения об изменения свойства</summary>
        public event PropertyChangedEventHandler PropertyChanged;
 
        /// <summary>Метод для вызова события извещения об изменении свойства</summary>
        /// <param name="propertyName">Изменившееся свойство</param>
        public void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
        /// <summary>Метод для вызова события извещения об изменении свойства
        /// с передачей дополнительных данных</summary>
        /// <param name="propertyName">Изменившееся свойство</param>
        /// <param name="removeData">Удаляемые данные</param>
        /// <param name="addData">Добавляемые данные</param>
        public void OnPropertyChanged(string propertyName, object removeData, object addData) =>
                    PropertyChanged?.Invoke(this, new PropertyChangedDataEventArgs(propertyName, removeData, addData));
 
    }
Сама Модель
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
46
47
48
49
50
51
52
53
54
55
56
57
58
    public class RandomColorModel : OnPropertyChangedClass
    {
        private static readonly Random random = new Random();
        public static readonly IEnumerable<string> NamesAllColors = typeof(Brushes)
            .GetProperties()
            .Select(prop => prop.Name)
            .ToArray();
        public IEnumerable<string> RandomColors => randomColors;
        private List<string> randomColors = new List<string>();
 
        private void GenerateRandomColors(uint count)
        {
            lock (randomColors)
            {
                Thread.Sleep(2000);
                IEnumerable<string> colors = randomColors.ToArray();
                randomColors.Clear();
                OnPropertyChanged("RemoveColors", colors, null);
 
                Thread.Sleep(2000);
                colors = NamesAllColors.OrderBy(x => random.Next()).Take((int)count).ToArray();
                randomColors.AddRange(colors);
                OnPropertyChanged("AddColors", null, colors);
            }
        }
        public async void GenerateRandomColorsAsync(uint count)
            => await Task.Run(() => GenerateRandomColors(count));
 
        public async void AddColorsAsync(string color)
            => await Task.Run(() => AddColors(color));
        private void AddColors(string color)
        {
            lock (randomColors)
            {
                Thread.Sleep(2000);
                if (!randomColors.Contains(color) && NamesAllColors.Contains(color))
                {
                    randomColors.Add(color);
                    OnPropertyChanged("AddColors", null, new string[] { color });
                }
            }
        }
 
        public async void RemoveColorsAsync(string color)
            => await Task.Run(() => RemoveColors(color));
        private void RemoveColors(string color)
        {
            Thread.Sleep(2000);
            lock (randomColors)
            {
                if (randomColors.Contains(color))
                {
                    randomColors.Add(color);
                    OnPropertyChanged("RemoveColors", new string[] { color }, null);
                }
            }
        }
    }
ViewModel.
Модель создаётся в Task.
К ней подключается прослушка на событие PropertyChanged.
В обработчике получаются данные для изменения коллекции в потоке Модели, но изменение коллекции производится в основном потоке.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    public class RandomColorVM : OnPropertyChangedClass
    {
        private uint _countColors;
        private string _selectColor;
        private RelayCommand _selectColorCommand;
        RandomColorModel model/* = new RandomColorModel()*/;
        private RelayCommand _addColorCommand;
        private RelayCommand _removeColorCommand;
 
        public ObservableCollection<string> RandomColors { get; } = new ObservableCollection<string>();
        public uint CountColors { get => _countColors; set { _countColors = value; OnPropertyChanged(); } }
 
        public string SelectColor { get => _selectColor; set { _selectColor = value; OnPropertyChanged(); } }
        public RelayCommand GenerateRandomColorsCommand => _selectColorCommand ?? (_selectColorCommand = new RelayCommand(GenerateRandomColorsMetod, GenerateRandomColorsCanMetod));
        public RelayCommand AddColorCommand => _addColorCommand ?? (_addColorCommand = new RelayCommand(AddRandomColorsMetod, AddRandomColorsCanMetod));
 
        private bool AddRandomColorsCanMetod(object parameter)
            => RandomColorModel.NamesAllColors.Contains(parameter?.ToString());
 
        private void AddRandomColorsMetod(object parameter)
        {
            if (AddRandomColorsCanMetod(parameter))
                model.AddColorsAsync(parameter?.ToString());
        }
        public RelayCommand RemoveColorCommand => _removeColorCommand ?? (_removeColorCommand = new RelayCommand(RemoveRandomColorsMetod, RemoveRandomColorsCanMetod));
 
        private bool RemoveRandomColorsCanMetod(object parameter)
            => RandomColors.Contains(parameter?.ToString());
 
        private void RemoveRandomColorsMetod(object parameter)
        {
            if (RemoveRandomColorsCanMetod(parameter))
                model.RemoveColorsAsync(parameter?.ToString());
        }
 
        private bool GenerateRandomColorsCanMetod(object parameter)
            => uint.TryParse(parameter?.ToString(), out uint _tmp);
 
        private void GenerateRandomColorsMetod(object parameter)
        {
            if (GenerateRandomColorsCanMetod(parameter))
                model.GenerateRandomColorsAsync(uint.Parse(parameter.ToString()));
            SelectColor = null;
        }
 
        public RandomColorVM()
        {
            Task task = new Task(()=> model= new RandomColorModel());
            task.Start();
            task.Wait();
            model.PropertyChanged += Model_PropertyChanged;
            GenerateRandomColorsMetod(CountColors = 5);
        }
 
        private void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
 
            if ((string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == "RemoveColors")
                        && e is PropertyChangedDataEventArgs eR
                        && eR.RemoveData is IEnumerable<string> removeColors)
 
                Application.Current.Dispatcher.BeginInvoke(new Action(
                    () =>
                    {
                        foreach (string color in removeColors)
                        {
                            if (RandomColors.Contains(color))
                                RandomColors.Remove(color);
                        }
 
                    }),
                    null
                );
 
            if ((string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == "AddColors")
                        && e is PropertyChangedDataEventArgs eA
                        && eA.AddData is IEnumerable<string> addColors)
 
                Application.Current.Dispatcher.BeginInvoke(new Action(
                    () =>
                    {
                        foreach (string color in addColors)
                        {
                            if (!RandomColors.Contains(color))
                                RandomColors.Add(color);
                        }
 
                    }),
                    null
                );
        }
 
 
    }
View для демонстрации
XML
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<Window x:Class="CyberForum.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CyberForum"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <local:RandomColorVM/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Border Background="AliceBlue" Margin="5" Padding="5">
                <Grid VerticalAlignment="Center" >
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="Количество цветов: "/>
                    <TextBox Grid.Column="1" Text="{Binding CountColors}" HorizontalContentAlignment="Center"/>
                    <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Генерация нового набора" 
                            Command="{Binding GenerateRandomColorsCommand}" 
                            CommandParameter="{Binding CountColors}"/>
                </Grid>
            </Border>
            <Border Background="AliceBlue" Margin="5" Padding="5">
                <Grid VerticalAlignment="Center" >
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="Цвет: "/>
                    <TextBox x:Name="textBox" Grid.Column="1"  HorizontalContentAlignment="Center"/>
                    <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Добавить цвет в набор" 
                        Command="{Binding AddColorCommand}" 
                        CommandParameter="{Binding Text, ElementName=textBox}"/>
                </Grid>
            </Border>
            <Border Background="AliceBlue" Margin="5" Padding="5">
                <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Удалить выделенный цвет" 
                        Command="{Binding RemoveColorCommand}" 
                        CommandParameter="{Binding SelectColor}"/>
            </Border>
        </StackPanel>
        <StackPanel Grid.Column="1">
                    <TextBlock Text="Список цветов" HorizontalAlignment="Center"/>
        <ListBox  ItemsSource="{Binding RandomColors}" SelectedItem="{Binding SelectColor}"/>
 
        </StackPanel>
        <Border Grid.Column="2" Grid.RowSpan="2"  Background="{Binding SelectColor}">
            <TextBlock Text="{Binding SelectColor}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Border>
    </Grid>
</Window>
0
215 / 149 / 48
Регистрация: 28.12.2016
Сообщений: 716
05.08.2019, 22:36
Элд Хасп, на просторах интернета видел СoncurrentObservableCollection<T>, с ней будет проще, ибо вся логика по lock и перенаправлению дистпачера осуществляется в ней.


Примерно такое

Кликните здесь для просмотра всего текста

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/// <summary>
    /// Concurrent collection that emits change notifications on a dispatcher thread
    /// </summary>
    /// <typeparam name="T">The type of objects in the collection</typeparam>
    [Serializable]
    [ComVisible(false)]
    [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
    public class ObservableConcurrentBag<T> : IProducerConsumerCollection<T>,
        IEnumerable<T>, ICollection, IEnumerable
    {
        /// <summary>
        /// The dispatcher on which event notifications will be raised
        /// </summary>
        private readonly Dispatcher dispatcher;
 
        /// <summary>
        /// The internal concurrent bag used for the 'heavy lifting' of the collection implementation
        /// </summary>
        private readonly ConcurrentBag<T> internalBag;
 
        /// <summary>
        /// Initializes a new instance of the ConcurrentBag<T> class that will raise <see cref="INotifyCollectionChanged"/> events
        /// on the specified dispatcher
        /// </summary>
        public ObservableConcurrentBag(Dispatcher dispatcher)
        {
            this.dispatcher = dispatcher;
            this.internalBag = new ConcurrentBag<T>();
        }
 
        /// <summary>
        /// Initializes a new instance of the ConcurrentBag<T> class that contains elements copied from the specified collection 
        /// that will raise <see cref="INotifyCollectionChanged"/> events on the specified dispatcher
        /// </summary>
        public ObservableConcurrentBag(Dispatcher dispatcher, IEnumerable<T> collection)
        {
            this.dispatcher = dispatcher;
            this.internalBag = new ConcurrentBag<T>(collection);
        }
 
        /// <summary>
        /// Occurs when the collection changes
        /// </summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;
 
        /// <summary>
        /// Raises the <see cref="CollectionChanged"/> event on the <see cref="dispatcher"/>
        /// </summary>
        private void RaiseCollectionChangedEventOnDispatcher(NotifyCollectionChangedEventArgs e)
        {
            this.dispatcher.BeginInvoke(new Action<NotifyCollectionChangedEventArgs>(this.RaiseCollectionChangedEvent), e);
        }
 
        /// <summary>
        /// Raises the <see cref="CollectionChanged"/> event
        /// </summary>
        /// <remarks>
        /// This method must only be raised on the dispatcher - use <see cref="RaiseCollectionChangedEventOnDispatcher" />
        /// to do this.
        /// </remarks>
        private void RaiseCollectionChangedEvent(NotifyCollectionChangedEventArgs e)
        {
            this.CollectionChanged(this, e);
        }
 
        #region Members that pass through to the internal concurrent bag but also raise change notifications
 
        bool IProducerConsumerCollection<T>.TryAdd(T item)
        {
            bool result = ((IProducerConsumerCollection<T>)this.internalBag).TryAdd(item);
            if (result)
            {
                this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
            }
            return result;
        }
 
        public void Add(T item)
        {
            this.internalBag.Add(item);
            this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }
 
        public bool TryTake(out T item)
        {
            bool result = this.TryTake(out item);
            if (result)
            {
                this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
            }
            return result;
        }
 
        #endregion
 
        #region Members that pass through directly to the internal concurrent bag
 
        public int Count
        {
            get
            {
                return this.internalBag.Count;
            }
        }
 
        public bool IsEmpty
        {
            get
            {
                return this.internalBag.IsEmpty;
            }
        }
 
        bool ICollection.IsSynchronized
        {
            get
            {
                return ((ICollection)this.internalBag).IsSynchronized;
            }
        }
 
        object ICollection.SyncRoot
        {
            get
            {
                return ((ICollection)this.internalBag).SyncRoot;
            }
        }
 
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return ((IEnumerable<T>)this.internalBag).GetEnumerator();
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this.internalBag).GetEnumerator();
        }
 
        public T[] ToArray()
        {
            return this.internalBag.ToArray();
        }
 
        void IProducerConsumerCollection<T>.CopyTo(T[] array, int index)
        {
            ((IProducerConsumerCollection<T>)this.internalBag).CopyTo(array, index);
        }
 
        void ICollection.CopyTo(Array array, int index)
        {
            ((ICollection)this.internalBag).CopyTo(array, index);
        }
 
        #endregion
    }
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
05.08.2019, 23:11
Цитата Сообщение от Defences Посмотреть сообщение
на просторах интернета видел.....
Реализаций очень много.
И выбор той или иной больше продиктован корпоративными правилами.

По мне, я чаще реализую две отдельные коллекции ObservableCollection в Модели и VM. В VM ObservableCollection RefadOnly (у свойства только геттер) и подключаю прослушку к INCC ObservableCollection Модели. И из прослушки изменяю коллекцию VM.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16115 / 11236 / 2887
Регистрация: 21.04.2018
Сообщений: 33,036
Записей в блоге: 2
06.08.2019, 00:29
Defences, imcavs™, кстати, прочитайте https://msdn.microsoft.com/ru-... 2147217396.

Если выходить за рамки WPF - то подобное приведение к основному потоку потребно и для INPC.
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
06.08.2019, 00:29
Помогаю со студенческими работами здесь

Data Binding файла xml: ComboBox остается пустым
Сделал так: в проект добавил папку DataSorce и положил туда &lt;?xml version=”1.0″ encoding=”utf-8″ ?&gt; &lt;companies&gt; ...

Combobox filter WPF MVVM
Всем привет. Есть два combobox(к примеру) у которых {binding Person}. Person находится в ObservableCollection. Хочу у первого...

WPF MVVM Prism ComboBox не правильно работает привязка SelectedItem
Итак есть Datagrid у него в качестве колонки указано: &lt;DataGridTemplateColumn Header=&quot;Валюта&quot;&gt; ...

WPF команды и MVVM. Часть 1. [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Для использования и создания WPF команд в Net предусмотрен...

MVVM, непонятки c binding-ом
WPF + MVVM light Не работает связывание свойств ViewModel с View. При открытии формы отрабатывает локатор - это видно по логам и...


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

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru