Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.66/29: Рейтинг темы: голосов - 29, средняя оценка - 4.66
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
.NET 4.x

Потоко-безопасный ObservableCollection с AddRange

15.09.2017, 16:09. Показов 5766. Ответов 9

Студворк — интернет-сервис помощи студентам
Вообщем, я заполняю в нескольких потоках массив. на данный момент использую "List<ExpandoObject> Вывод" из-за "AddRange"(щас отлаживаю скорость работы по обработке данных). Он заполняется достаточно долго, а содержимое надо отображать "как подгрузилось". Т.е. скажем 30000 записей всего, а отображение делается каждую "1000(тысячную)" запись. Ну. Запустил поток, он порождает несколько потоков(в зависимости от процессора) и оно в свою очередь(эти несколько потоков) "строят кусочек 1000" во временный массив. После прохода по текущей "1000"(несколько потоков закончились) происходит запись в "List" через AddRange(Скажем: 5 потоков - записывается результат из каждого в List<ExpandoObject> через AddRange). Но надо это отобразить(отображать, когда кусок "1000" занесено в List<ExpandoObject> Вывод) в контроле(RadGridView - это типо DataGrid-контрол в Telerik). Но у него не реализован "INotifyPropertyChanged". А ObservableCollection есть, но он "не потоко-безопасный".

Собственно вопрос: какой выход из ситуации?

В инете нашёл "AddRange" для ObservableCollection.
Кликните здесь для просмотра всего текста

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
    /// <summary> 
    /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    public class ObservableRangeCollection<T> : ObservableCollection<T>
    {
        /// <summary> 
        /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). 
        /// </summary> 
        public void AddRange(IEnumerable<T> collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
 
            /*collection.Dispatcher.BeginInvoke(new Action(delegate ()
            {
                KOHTPOL_CnuCOK_TOBAPOB.Items.Refresh();
                this.KOHTPOL_CnuCOK_TOBAPOB.GroupDescriptors.Clear();
                this.KOHTPOL_CnuCOK_TOBAPOB.GroupDescriptors.AddRange(Группировка);
            }));*/
            lock (collection)
            {
            foreach (var i in collection.ToArray()) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 
            }
        }
 
        /// <summary> 
        /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). 
        /// </summary> 
        public void RemoveRange(IEnumerable<T> collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
 
            foreach (var i in collection) Items.Remove(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
 
        /// <summary> 
        /// Clears the current collection and replaces it with the specified item. 
        /// </summary> 
        public void Replace(T item)
        {
            ReplaceRange(new T[] { item });
        }
 
        /// <summary> 
        /// Clears the current collection and replaces it with the specified collection. 
        /// </summary> 
        public void ReplaceRange(IEnumerable<T> collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
 
            Items.Clear();
            foreach (var i in collection) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
 
        /// <summary> 
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. 
        /// </summary> 
        public ObservableRangeCollection()
            : base() { }
 
        /// <summary> 
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. 
        /// </summary> 
        /// <param name="collection">collection: The collection from which the elements are copied.</param> 
        /// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception> 
        public ObservableRangeCollection(IEnumerable<T> collection)
            : base(collection) { }
    }

Как сделать "Потоко-Безопасным"?


*ExpandoObject нужен т.к. количество "колонок" переменчивое, в зависимости от того, на что ткнул я.

Добавлено через 4 часа 38 минут
Или может другой вопрос: как отдать команду контролу на переотрисовку содержимого(в случае когда "List<ExpandoObject> Вывод" является ItemsSource)?
***.Items.Refresh(); не работает(Но вот перезадание ItemsSource или перегрупировка - отрисовывают содержимое. Только проблема: мне надо сохранить то, куда "я прокрутил", и только в конец добавлять. Т.е. если я раскрыл группировку - то она должна остаться раскрытой.).


Для упрощения, т.к. не все могли работать с "RadGridView", предлагаю говорить об "DataGrid". Думаю это поможет найти ответ.

Добавлено через 7 минут
Может быть похожий вопрос задавался? Я буду рад любой информации. Конечно же я был в поисковике и видел что-то типа
https://msdn.microsoft.com/ru-... .110).aspx
Только я не понял: как такое применить к "ExpandoObject". В смысле на примере. Он без свойств. Динамический.

Также видел
Кликните здесь для просмотра всего текста
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
Имеется ObservableCollection, в которой хранятся следующие данные:
public class StatisticsModel : ObservableObject
{
  private long incomingTraffic;
  private long outcomingTraffic;
  private string time;
  
  public string Time
  {
    get { return this.time; }
    set { this.Set("Time", ref this.time, value); }
  }
 
  public long IncomingTraffic
  {
    get { return this.incomingTraffic; }
    set { this.Set("IncomingTraffic", ref this.incomingTraffic, value); }
  }
 
  public long OutcomingTraffic
  {
    get { return this.outcomingTraffic; }
    set { this.Set("OutcomingTraffic", ref this.outcomingTraffic, value); }
  }
}
 
Есть ViewModel, где DispatchingObservableCollection есть ничто иное, как потоко-безопасный ObservableCollection. а network.Statistics это по сути часть реализации паттерна Наблюдатель:
public class StatisticsViewModel : ViewModelBase
{
  private DispatchingObservableCollection<StatisticsModel> statisticsCollection;
 
  public StatisticsViewModel(INetwork network)
  {
    this.statisticsCollection = new DispatchingObservableCollection<StatisticsModel>();
 
    network.Service.Statistics += stats => this.StatisticsCollection.Add(Mapper.Map<StatisticsModel>(stats));
  }
 
  public DispatchingObservableCollection<StatisticsModel> StatisticsCollection
  {
    get { return this.statisticsCollection; }
    set { this.Set("StatisticsCollection", ref this.statisticsCollection, value); }
  }
}
 
А так же есть View:
<Window DataContext="{Binding Statistics, Source={StaticResource Locator}}" ...>
    <Grid>
    ...
        <DataGrid ItemsSource="{Binding StatisticsCollection}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Time}" Header="Время"/>
                <DataGridTextColumn Binding="{Binding IncomingTraffic}" Header="Входящий траффик"/>
                <DataGridTextColumn Binding="{Binding OutcomingTraffic}" Header="Исходящий траффик"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
 
Данные с сервера приходят примерно такие:
{ Time: "00:00:01", IncomingTraffic: 100, OutcomingTraffic: 0 },
{ Time: "00:00:01", IncomingTraffic: 0, OutcomingTraffic: 100 },
{ Time: "00:00:01", IncomingTraffic: 100, OutcomingTraffic: 0 },
{ Time: "00:00:02", IncomingTraffic: 100, OutcomingTraffic: 0 },
{ Time: "00:00:02", IncomingTraffic: 0, OutcomingTraffic: 100 }
 
По сути, все это дело отлично группируется:
this.statisticsCollection.GroupBy(x => x.Time)
  .Select(
    y =>
    new StatisticsModel
      {
        Time = y.First().Time,
        IncomingTraffic = y.Sum(z => z.IncomingTraffic),
        OutcomingTraffic = y.Sum(z => z.OutcomingTraffic)
      });

(ссылка не видна. Вот скопировал.)
Но почему-то у меня подчёркивает красным "DispatchingObservableCollection" и "StatisticsModel". Что я не подключил?

Не по теме:

Я просто, вернувшись к своему сообщению - прочитал и понял что надо "дописать инфу"

0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
15.09.2017, 16:09
Ответы с готовыми решениями:

Как отобразить ObservableCollection<ObservableCollection<Page>> ?
Предположим у меня есть класс VM: public class VM { public ObservableCollection&lt;ObservableCollection&lt;Page&gt;&gt;...

Не безопасный код, почему он не безопасный.
Почему использование указателей в с# считается небезопасным?

Потоко-независимая очередь записывает 2е команды в одну ячейку. Почему ?
Доброго времени суток. Написал 2а приложения общяющихся между собой по TCP. Одно является сервером(планшет), второе клиент(windows). ...

9
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
16.09.2017, 17:10  [ТС]
Может я вообще неправильно подошёл к вопросу? Главная цель: Нажал на кнопку - начался вывод кусками. по 1000 штук. Т.е. запустился фоновой поток и в конец DataGrid добавляет элементы. Которые должны группироваться. При этом не трогая уже нарисованные группы(т.е. я открываю группы, а оно в конец продолжает добавлять элементы\НовыеГруппы с элементами).
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
17.09.2017, 17:08
Лучший ответ Сообщение было отмечено edward_freedom как решение

Решение

47911, я когда-то давно задавался подобным вопросом. Насколько я помню, я даже находил готовую реализацию VirtualizingObservableCollection. Суть в том, что когда ты прокручиваешь список, то данные в коллекцию в фоне подгружаются. В общем, погугли.

Вот ещё может быть полезно: Виртуализация

Добавлено через 3 минуты
Нашёл. Вот этот пример мне попадался. Только он был не на хабре, в принципе, не суть
https://habrahabr.ru/post/208792/

Добавлено через 50 секунд
Точно, вот исходник https://www.codeproject.com/Ar... ualization

Добавлено через 6 минут
Цитата Сообщение от 47911 Посмотреть сообщение
Т.е. скажем 30000 записей всего, а отображение делается каждую "1000(тысячную)" запись.
Вот это не понял, что значит "отображение"? Добавление в коллекцию? Или создание новой коллекции и её присваивание RadGridView, который не отображает новую коллекцию?

Цитата Сообщение от 47911 Посмотреть сообщение
Но надо это отобразить(отображать, когда кусок "1000" занесено в List<ExpandoObject> Вывод) в контроле(RadGridView - это типо DataGrid-контрол в Telerik). Но у него не реализован "INotifyPropertyChanged".
Есть подозрение, что ты что-то делаешь неправильно.

Слушай, ты бы чуток подробнее изъяснялся, а то несколько раз перечитывал и всё равно есть вопросы. Подробнее - это называть, что откуда и куда не упуская детали (из коллекции Один в коллекцию Два, чтобы отобразить в RadGridView , вероятно, мимо прошло много людей по причине не полного понимания). Как я понял List<ExpandoObject> Вывод прибинден к RadGridView .
2
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
17.09.2017, 20:41  [ТС]

Не по теме:


Цитата Сообщение от Casper-SC Посмотреть сообщение
47911, я когда-то давно задавался подобным вопросом.
Которым? "Может я вообще неправильно подошёл к вопросу?" или "Что я вообще тут делаю?".



Casper-SC, ваши ссылки - как раз по теме. Только вот проблема ".NET 4.0" потолок. Однако:
"RadGridView от Telerik" обладает "VirtualizingStackPanel.IsVirtualizing=" True"". В то время как "DataGrid" в ".NET 4.0" нет. Поэтому и "RadGridView" т.к. он типо доработаный "DataGrid"(настолько, что прям слёзы наворачиваются). Поэтому я и сказал "Для упрощения, т.к. не все могли работать с "RadGridView", предлагаю говорить об "DataGrid"".

Логика программы:
C#
1
Грид.ItemsSource = Вывод;
1) Нажал на кнопку
2) Запустился фоновой поток
3) Он, сделав некоторые приготовления, запускает несколько фоновых(для упрощения -> 5 шт.)
4) Каждый поток подключается к базе и высасывает "Свою 200 шт." товаров, и записывает в "List<ExpandoObject> Кусок"
5) после отработки всех 5 потоков - запуск 2 стадии - записываем всё из "List<ExpandoObject> Кусок" в "List<ExpandoObject> Вывод".
5.1) Пытаемся сказать "Грид" что у нас изменения в "List<ExpandoObject> Вывод". "Грид.Items.Refresh();" не сработало. Мой мозг говорит, что так:
C#
1
2
Грид.ItemsSource = null;
Грид.ItemsSource = Вывод;
или так(перегруппировка. в "Группировка00" содержится приказ группировки по колонке):
C#
1
2
Грид.GroupDescriptors.Clear();
Грид.GroupDescriptors.AddRange(Группировка00);
Делать не надо. Т.к. высасываем по 1000 и после добавления в "List<ExpandoObject> Вывод" мы очищаем "List<ExpandoObject> Кусок" и проверяем "есть ли ещё товары". Если есть - идём ещё раз повторять процедуру с шага (3).

Собственно надо чтобы после добавления "1000" человек мог продолжать работать, и максимум что видеть "появляются какие-то записи". В конце. Т.е. тыкает на кнопки, раскрывает группы - и радуется. А если делать так, как мой мозг говорил "не делать" - сбрасываются "Раскрытые группировки"(в смысле закрываются, т.к. переотрисовка), то, что натыкал - исчезает(визуально. Сама прога то отлавливает тыки, но визуально всё пропадает). Вообщем пытаюсь сделать удобство работы с 1 000 000 записями. Выводя их постепенно.

Может я подошёл не правильно к этой задаче? Постраничный вывод - не вариант.

Можно использовать "ObservableRangeCollection<ExpandoObject > Вывод"("В инете нашёл "AddRange" для ObservableCollection." - первый пост), но он "Потоко не безопасный". Т.к. в ситуации когда его дёргают несколько потоков - он убивает программу. Да и не уверен что он будет "Оповещать Грид" только когда "AddRange" закончится, а не после "каждой итерации"(не проверил, т.к. несколько потоков - и он убивает прогу).

Я достаточно понятно описал проблему и мой ход мыслей?
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
17.09.2017, 21:30
47911, интересует, почему используется не ObservableCollection<T>, а List<T>? Потому что в List<T> есть AddRange и он не валит программу при добавлении элементов в коллекцию?

Так, то есть нужно уметь в коллекцию добавлять сразу диапазон значений и после добавления оповещать UI, что мы добавили. Ок.
Нужно уметь добавлять в коллекцию значения из нескольких потоков.

Добавлено через 3 минуты
Цитата Сообщение от 47911 Посмотреть сообщение
Я достаточно понятно описал проблему и мой ход мыслей?
На это раз просто в разы понятнее. Так и нужно.

Добавлено через 6 минут
Я нагуглил такие дела:
https://mvvmutils.codeplex.com... lection.cs
https://www.codeproject.com/Ti... Collection

Добавлено через 2 минуты
Можно взять за основу ConcurrentObservableCollection<T> и добавить туда AddRange так, чтобы коллекция не оповещала о своём изменении до конца выполнения AddRange.

Добавлено через 2 минуты
По крайней мере я посмотрел пример, и увидел, что в коллекцию там добавляются данные не из UI потока и всё работает, осталось допилить недостающий метод. Это самое быстрое решение в плане трудозатрат.

Добавлено через 2 минуты
Кстати, ничего не нужно допиливать, всё уже есть:
C#
1
2
3
4
5
6
7
       /// <summary>
        /// This method adds the given generic list of items
        /// as a range into current collection by casting them as type T.
        /// It then notifies once after all items are added.
        /// </summary>
        /// <param name="items">The source collection.</param>
        public void Add(IEnumerable<T> items)
Добавлено через 1 минуту
И ещё, первый исходник, что ты показал вызывает сомнения с его локом входящей коллекции. А вот эти исходники, что я нагуглил внушают доверие. Правда, понятно, что я мельком посмотрел, не изучал пол дня

Добавлено через 4 минуты
Отпишись хоть потом, что и как. Интересно, как в итоге проблема решится.
1
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
17.09.2017, 21:33  [ТС]
Цитата Сообщение от Casper-SC Посмотреть сообщение
47911, интересует, почему используется не ObservableCollection<T>, а List<T>? Потому что в List<T> есть AddRange и он не валит программу при добавлении элементов в коллекцию?
Имено.

Цитата Сообщение от Casper-SC Посмотреть сообщение
Можно взять за основу ConcurrentObservableCollection<T> и добавить туда AddRange так, чтобы коллекция не оповещала о своём изменении до конца выполнения AddRange.
"AddRange" Будет выглядеть так?
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
157
158
159
160
161
162
163
164
165
using Pellared.Common;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Windows.Threading;
 
namespace Pellared.MvvmUtils
{
    public class ConcurrentObservableCollection<T> : ObservableCollection<T>
    {
        /// <summary>
        /// This private variable holds the flag to
        /// turn on and off the collection changed notification.
        /// </summary>
        private bool suspendCollectionChangeNotification;
 
        /// <summary>
        /// This event is overridden CollectionChanged event of the observable collection.
        /// </summary>
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
 
        /// <summary> 
        /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). 
        /// </summary> 
        public void AddRange(IEnumerable<T> collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
 
            foreach (var i in collection.ToArray()) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
 
        }
 
        /// <summary> 
        /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). 
        /// </summary> 
        public void RemoveRange(IEnumerable<T> collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
 
            foreach (var i in collection) Items.Remove(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
 
        /// <summary>
        /// This method adds the given generic list of items
        /// as a range into current collection by casting them as type T.
        /// It then notifies once after all items are added.
        /// </summary>
        /// <param name="items">The source collection.</param>
        public void Add(IEnumerable<T> items)
        {
            Ensure.NotNull(items, "items");
 
            SuspendCollectionChangeNotification();
            try
            {
                foreach (T item in items)
                {
                    Add(item);
                }
            }
            finally
            {
                NotifyChanges();
            }
        }
 
        /// <summary>
        /// This method removes the given generic list of items as a range
        /// into current collection by casting them as type T.
        /// It then notifies once after all items are removed.
        /// </summary>
        /// <param name="items">The source collection.</param>
        public void Remove(IEnumerable<T> items)
        {
            Ensure.NotNull(items, "items");
 
            SuspendCollectionChangeNotification();
            try
            {
                foreach (T item in items)
                {
                    Remove(item);
                }
            }
            finally
            {
                NotifyChanges();
            }
        }
 
        /// <summary>
        /// Resumes collection changed notification.
        /// </summary>
        public void ResumeCollectionChangeNotification()
        {
            suspendCollectionChangeNotification = false;
        }
 
        /// <summary>
        /// Suspends collection changed notification.
        /// </summary>
        public void SuspendCollectionChangeNotification()
        {
            suspendCollectionChangeNotification = true;
        }
 
        /// <summary>
        /// Raises collection change event.
        /// </summary>
        public void NotifyChanges()
        {
            ResumeCollectionChangeNotification();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            OnCollectionChanged(arg);
        }
 
        /// <summary>
        /// This collection changed event performs thread safe event raising.
        /// </summary>
        /// <param name="e">The event argument.</param>
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            // Recommended is to avoid reentry
            // in collection changed event while collection
            // is getting changed on other thread.
            using (BlockReentrancy())
            {
                if (!suspendCollectionChangeNotification)
                {
                    NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
                    if (eventHandler != null)
                    {
                        OnCollectionChanged(eventHandler, e);
                    }
                }
            }
        }
 
        private void OnCollectionChanged(NotifyCollectionChangedEventHandler eventHandler, NotifyCollectionChangedEventArgs e)
        {
            // Walk through invocation list.
            Delegate[] delegates = eventHandler.GetInvocationList();
            foreach (NotifyCollectionChangedEventHandler handler in delegates)
            {
                // If the subscriber is a DispatcherObject and different thread.
                var dispatcherObject = handler.Target as DispatcherObject;
                if (dispatcherObject != null && !dispatcherObject.CheckAccess())
                {
                    // Invoke handler in the target dispatcher's thread...
                    // asynchronously for better responsiveness.
                    dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, e);
                }
                else
                {
                    // Execute handler as is.
                    handler(this, e);
                }
            }
        }
    }
}
Цитата Сообщение от Casper-SC Посмотреть сообщение
Добавлено через 3 минуты
Сообщение от 47911
Я достаточно понятно описал проблему и мой ход мыслей?
На это раз просто в разы понятнее. Так и нужно.
Ну. Я поспал.


В течении 12-14 часов опробую, т.к. время очень позднее.

И хотел бы я уточнить: можно же в DataGrid'е указать группировку 1 раз, чтобы при добавлении "Новых данных"(товаров) - они сразу попадали в группировку?
Что было у меня:
Загружается программа, что-то делает, + это:
C#
1
2
3
4
5
6
7
                Группировка = new Telerik.Windows.Data.GroupDescriptorCollection();
                Группировка.AddRange(Грид.GroupDescriptors);
                Грид.GroupDescriptors.Add(new Telerik.Windows.Controls.GridView.ColumnGroupDescriptor()
                {
                    Column = Грид.Columns["Категория"],
                    SortDirection = ListSortDirection.Ascending
                });
Ну. Думаю всё ок. Делаю "Грид.ItemsSource = Вывод;". В "Вывод" записываю данных. А оно их не группирует. Мне приходится перезадавать группировку(т.е. выводить раздел и перезадавать группировку. Это безумие просто. . .). Что я делаю не так?
Что в самом гриде:
XML
1
2
3
4
5
6
7
                <telerik:RadGridView.GroupDescriptors>
                    <telerik:GroupDescriptor Member="Категория" SortDirection="Ascending">
                        <telerik:GroupDescriptor.AggregateFunctions>
                            <telerik:CountFunction Caption="Количество: "/>
                        </telerik:GroupDescriptor.AggregateFunctions>
                    </telerik:GroupDescriptor>
                </telerik:RadGridView.GroupDescriptors>
(я понимаю, что это может тянуть на отдельную тему. Но рискну задать вопрос.)

*Я смотрел пример https://docs.microsoft.com/en-... id-control и даже запускал у себя. Но что-то не вкурил.*
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
17.09.2017, 21:36
Кстати, я смотрел вот это:
https://www.codeproject.com/Ar... ary-and-So
А привёл другие ссылки. Там, думаю, тоже рабочие варианты.

Добавлено через 2 минуты
Цитата Сообщение от 47911 Посмотреть сообщение
"AddRange" Будет выглядеть так?
AddRange там уже есть: void Add(IEnumerable<T> items)

Убери эти AddRange и RemoveRange, они там вообще ни к месту и не факт, что не нарушат работу коллекции.
1
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
17.09.2017, 21:44  [ТС]
Casper-SC, а опыт с группировкой DataGrid есть? Можешь подсказать "как заставить его всегда группировать данные"(сказать ему это 1 раз и забыть об этом)?

*Возможно этот код лежит на поверхности. Просплюсь и пересмотрю примеры с MSDN ещё раз.*
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
17.09.2017, 21:54
Цитата Сообщение от 47911 Посмотреть сообщение
Casper-SC, а опыт с группировкой DataGrid есть?
Не, с ходу не подскажу, я с WPF вообще уже долго не работал, нужно разбираться.
0
2 / 2 / 0
Регистрация: 18.06.2016
Сообщений: 143
25.09.2017, 05:45  [ТС]
Цитата Сообщение от 47911 Посмотреть сообщение
*Возможно этот код лежит на поверхности. Просплюсь и пересмотрю примеры с MSDN ещё раз.*
Выпало время, пишу. Всё действительно просто, вчитываться надо(смотреть и медетировать до просветления).
Пример:
C#
1
2
3
4
5
KOHKyPEHT_ObservableCollection<ExpandoObject> Список_Вывод;
ICollectionView Список_Вывод_Окончательный;
            Список_Вывод_Окончательный = CollectionViewSource.GetDefaultView(Список_Вывод);
            Список_Вывод_Окончательный.GroupDescriptions.Add(new PropertyGroupDescription("Категория"));
KOHTPOL_CnuCOK.ItemsSource = Список_Вывод_Окончательный;
И всё работает. С сортировкой и фильтрацией, по идее, должно быть аналогично.



А по поводу "Главного Вопроса" этой темы - рекомендация уже была дана, смотрите выше.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.09.2017, 05:45
Помогаю со студенческими работами здесь

Parameters.addRange
Подскажите пожалуйста как сократить такую запись cmd.CommandText = @&quot;INSERT INTO Izdelie (id_izd,Name,Цена,Высота,Ширина,Глубина,)...

.Items.AddRange
private void button6_Click_1(object sender, EventArgs e) { string names = new string; for (int i = 0;...

HttpWebRequest не воспринимает AddRange
Всем доброго времени суток. Возникла проблема с использование HttpWebRequest, а именно указания в нём диапазона байт, получаемых в...

List<Person> и AddRange
Как изменить следующий код, чтобы заработал ?.. using System; using System.Collections.Generic; class Program { static...

метод Add и AddRange
разбираюсь с ArrayList а именно на данном этапе смотрю методы Add и AddRange, вот например код ArrayList myStuff = new ArrayList(); ...


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

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
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