我有一个DataGrid
支持后面的代码中手动处理ObservableCollection<T>
。当我将新项目添加到支持列表或删除一个项目时,Count
已更新,但ItemsSource
未通知,因此Price
的总和更新。我甚至试过了BindingList<T>
,但即使是Count
也停止了更新!什么是黑客?
XAML:
<Label>
<Label.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}Count: {0}, Sum: {1}" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True">
<Binding ElementName="datagrid" Path="ItemsSource.Count" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="datagrid" Path="ItemsSource" Converter="{StaticResource SumConverter}" UpdateSourceTrigger="PropertyChanged" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Label.Content>
</Label>
SumConverter:
[ValueConversion(typeof(ObservableCollection<DocView>), typeof(String))]
public class SumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is ObservableCollection<DocView>)
{
ObservableCollection<DocView> items = (ObservableCollection<DocView>)value;
return items.Sum(x => x.Price);
}
else
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
代码背后:
ObservableCollection<DocView> list = new ObservableCollection<DocView>();
datagrid.DataContext = list;
答案 0 :(得分:0)
我认为问题在于您将列表绑定到datagrid
并且转换器无法获取ItemSource
中的更改,因为该属性未发生变化。该集合不会触发PropertyChanged
事件。试试这个:
datagrid.ItemsSource = list;
在你的XAML中使用多重绑定的转换器。绑定到ItemsSource.Count
将在集合更改时触发通知。请注意,您不需要将TextBlock
嵌入Label
。
<TextBlock Grid.Row="2">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding ElementName="datagrid" Path="ItemsSource" />
<Binding ElementName="datagrid" Path="ItemsSource.Count"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
显然你需要相应地修改你的转换器。
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values != null && values.Length > 0 && values[0] is ObservableCollection<string>)
{
ObservableCollection<DocView> items = (ObservableCollection<DocView>)values[0];
return string.Format("Count: {0}, Sum: {1}", items.Count, items.Sum(x => x.Price));
}
else
return "Count: 0, Sum: 0";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
这是一个需要最少更改的解决方案。更好的方法是将集合绑定到ViewModel而不是datagrid.ItemsSource
。
答案 1 :(得分:0)
完整的解决方案:
INPC
BindingList<T>
代替ObservableCollection<T>
(如果您使用ObservableCollection<T>
,则列表中现有项目属性的更新不会在BindingList<T>
时触发对UI的更新支持这个)XAML:
<DataGrid x:Name="datagrid" ItemsSource="{Binding List}" />
<Label>
<Label.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}Count: {0}, Sum: {1}" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True">
<Binding ElementName="datagrid" Path="DataContext.Count"/>
<Binding ElementName="datagrid" Path="DataContext.Total"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Label.Content>
</Label>
代码背后:
DocViewModel list= new DocViewModel();
datagrid.DataContext = list;
<强> DocViewModel 强>:
public class DocViewModel : INotifyPropertyChanged
{
public DocViewModel()
{
this.list = new BindingList<DocView>();
this.list.ListChanged += list_ListChanged;
}
void list_ListChanged(object sender, ListChangedEventArgs e)
{
OnPropertyChanged("Total");
OnPropertyChanged("Count");
}
public event PropertyChangedEventHandler PropertyChanged;
private BindingList<DocView> list;
public BindingList<DocView> List
{
get { return list; }
set
{
list = value;
OnPropertyChanged("List");
}
}
public decimal Total
{
get { return list.Sum(x => x.Price); }
}
private void OnPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int Count
{
get { return list.Count; }
}
}
<强> DocView:强>
public class DocView : INotifyPropertyChanged
{
private decimal price;
public decimal Price
{
get { return price; }
set
{
price = value;
OnPropertyChanged("Price");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
修改强>
这是一个更具体的视图模型通用实现,让您使用相同的逻辑,使用不同的项目类型来提供自定义的总计数和计数计算器:
public class GenericListViewModel<T> : INotifyPropertyChanged
{
Func<BindingList<T>, decimal> totalCalculator;
Func<BindingList<T>, int> countCalculator;
public GenericListViewModel(Func<BindingList<T>, decimal> _totalCalculator, Func<BindingList<T>, int> _countCalculator)
{
this.list = new BindingList<T>();
this.list.ListChanged += list_ListChanged;
this.totalCalculator = _totalCalculator;
this.countCalculator = _countCalculator;
}
void list_ListChanged(object sender, ListChangedEventArgs e)
{
OnPropertyChanged("Total");
OnPropertyChanged("Count");
}
public event PropertyChangedEventHandler PropertyChanged;
private BindingList<T> list;
public BindingList<T> List
{
get { return list; }
set
{
list = value;
OnPropertyChanged("List");
}
}
public decimal Total
{
get
{
if (totalCalculator != null)
return totalCalculator.Invoke(list);
else
throw new NotImplementedException("Total Func must be impelmented");
}
}
private void OnPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int Count
{
get
{
if (countCalculator != null)
return countCalculator.Invoke(list);
else
throw new NotImplementedException("Count Func must be implemented");
}
}
}
用法:
public class MyDocViewModel: GenericListViewModel<DocView>
{
public MyDocViewModel()
: base(
(list) =>
{
return list.Sum(x => x.Price);
},
(list) =>
{
return list.Count;
}
)
{
}
}