2014-07-11 16 views
0

我有一个DataGrid支持手动处理的ObservableCollection<T>后面的代码。当我将新项目添加到支持列表或删除项目时,将更新Count,但不会通知ItemsSource,以便更新Price的总和。我什至尝试BindingList<T>,但即使是Count停止更新!什么是黑客?ObservableCollection不反映摘要计算的变化

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

确保你的属性实现INotifyPropertyChanged – reggaeguitar

回答

0

我认为问题出在你的列表绑定到的方式datagrid,并且该转换器不会接受ItemSource中的更改,因为该属性不会更改。该集合不会触发PropertyChanged事件。试试这个:

datagrid.ItemsSource = list; 

然后在你的XAML中使用转换器进行多重绑定。绑定到ItemsSource.Count将在集合更改时触发通知。请注意,您不需要在Label中嵌入TextBlock

<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

+0

已经试过,没有工作。 –

+0

再次,不工作。问题出在INPC上。 –

+0

我编辑了我的答案。我自己尝试过,它适用于我。 –

0

完整的解决方案:

  1. 裹一切都在一个视图模型和实施INPC每一个地方
  2. 使用BindingList<T>,而不是ObservableCollection<T>(如果使用ObservableCollection<T>的更新列表中的现有项目的性质不会触发更新UI,而BindingList<T>支持)

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; 
     } 
     ) 
    { 

    } 
}