Я пытаюсь добавить группировку с промежуточной суммой в DataGrid. Прочитайте несколько статей: решение состоит в том, чтобы иметь ObservableCollection
с данными, обернуть их в CollectionViewSource
, что в свою очередь будет ItemsSource
для DataGrid. Промежуточный итог рассчитывается с помощью преобразователя, который получает Items
из CollectionViewGroup
в качестве входных данных и вычисляет сумму.
Все отлично работает только при начальной совокупности ObservableCollection
или при добавлении элемента создает новая группа. Но если элемент добавлен в какую-либо существующую группу, конвертер просто не вызывается для пересчета - очевидно, CollectionViewGroup.Items
не вызывает событие PropertyChanged
? Я немного просмотрел в Источник CollectionViewGroup - Items
, равный ReadOnlyObservableCollection<object>
, который должен вызывать PropertyChanged
после добавления элемента, не так ли?
Тогда я заметил, что CollectionViewGroup.ItemCount
отображается правильно после добавления новых элементов, поэтому я попробовал трюк с MultiBinding
- добавил конвертер IMultiValueConverter
, который принимает как Items
и ItemCount
в качестве параметров, ожидая, что ItemCount
вызовет пересчет. Это сработало, но снова безуспешно - каким-то образом преобразователь получает правильный вход только один раз, когда создается новая группа. Если элемент был добавлен в существующую группу, ItemCount
- это правильно, а Items
- нет! В коллекции Items
отсутствует добавленный элемент! Например, когда ItemCount
= 2, Items
имеют только 1 «старый» элемент (Items.Count
= 1). Когда ItemCount
= 3, Items
имеют только 2 "старых" элемента (Items.Count
= 2) и т. Д. c. Итак, опять же, преобразователь не может вычислить правильный промежуточный итог, потому что входные данные неполные ...
Похоже, что единственным рабочим решением было бы вызвать Refresh()
для всего CollectionViewSource
, но это расширяет все группы, вызывающие мерцание, нарушающие концепцию MVVM, так что это уродливо ...
Итак, мои вопросы:
Есть ли еще какие-либо изменения, чтобы сделать CollectionViewGroup.Items
повышение PropertyChanged
правильно?
Разве это не ошибка в CollectionViewGroup
, что Multi Converter получает Items.Count
= ItemCount
- 1?
Любой совет будет высоко оценен!
Полный пример кода включен GitHub
Ниже приведены некоторые выдержки из кода - XAML :
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock FontWeight="Bold" Text="Sum 1: " Margin="5,0,0,0"/>
<TextBlock FontWeight="Bold" >
<TextBlock.Text>
<Binding Path="Items" Converter="{StaticResource sumConverter}" ConverterParameter="AmountValue" StringFormat="{}{0:N2}"/>
</TextBlock.Text>
</TextBlock>
<TextBlock FontWeight="Bold" Text="Sum 2: " Margin="5,0,0,0"/>
<TextBlock FontWeight="Bold" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource sumMulConverter}" ConverterParameter="AmountValue" StringFormat="{}{0:N2}">
<Binding Path="Items"/>
<Binding Path="ItemCount"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
Преобразователи :
public class SumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == DependencyProperty.UnsetValue) return DependencyProperty.UnsetValue;
if (null == parameter) return null;
string propertyName = (string)parameter;
if (!(value is ReadOnlyObservableCollection<object>)) return null;
ReadOnlyObservableCollection<object> collection = (ReadOnlyObservableCollection<object>)value;
decimal sum = 0;
foreach (object o in collection)
{
sum += (decimal)o.GetType().GetProperty(propertyName).GetValue(o);
}
return sum;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class SumMulConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (null == parameter) return null;
if (!(parameter is string)) return null;
string propertyName = (string)parameter;
if (values == DependencyProperty.UnsetValue) return DependencyProperty.UnsetValue;
if (values == null) return null;
if (values.Length < 2) return null;
if (!(values[0] is ReadOnlyObservableCollection<object>)) return null;
ReadOnlyObservableCollection<object> collection = (ReadOnlyObservableCollection<object>)values[0];
if (!(values[1] is int)) return null;
Debug.Print($"ItemCount={(int)values[1]}; Collection Count = {collection.Count}");
decimal sum = 0;
foreach (object o in collection)
{
sum += (decimal)o.GetType().GetProperty(propertyName).GetValue(o);
}
return sum; //.ToString("N2", CultureInfo.CurrentCulture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}