2011-02-14 69 views
4

在我的小應用程序中,我有一個綁定到Measurement對象列表的DataGrid(請參見截圖)。 Measurement只是一個具有兩個屬性的數據容器:Date和CounterGas(float)。每個測量對象代表特定日期的氣體消耗量。DataGrid計算兩個數據綁定單元格中的值之間的差異

enter image description here

的測量值的表綁定到DataGrid如下:

<DataGrid ItemsSource="{Binding Path=Measurements}" AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" /> 
      <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" /> 
     </DataGrid.Columns> 
    </DataGrid> 

好了,現在我的問題:) 我想有旁邊的列另一列「計數器氣體」顯示實際計數器值和最後計數器值之間的差值。

E.g.這個額外的列應該計算2月13日和2月6日之間的差值=> 199.789 - 187.115 = 15.674

實現這個目標的最佳方法是什麼?我想避免Measurement類中的任何計算,它應該保存數據。我寧願更喜歡DataGrid來處理計算。 那麼有沒有辦法添加另一列,只是計算值之間的差異?也許使用某種轉換器和極端綁定? ; D

P.S .:也許某個聲譽較好的人可以嵌入屏幕截圖。謝謝:)

回答

4

極限綁定?沒問題。

<Window.Resources> 
    <local:ItemsDifferenceConverter x:Key="ItemsDifferenceConverter"/> 
</Window.Resources> 
<DataGrid ItemsSource="{Binding Path=Measurements}" AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" /> 
     <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" /> 
     <DataGridTextColumn Header="Difference"> 
      <DataGridTextColumn.Binding> 
       <MultiBinding Converter="{StaticResource ItemsDifferenceConverter}" Mode="OneWay"> 
        <Binding Path="."/> 
        <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="ItemsSource"/> 
       </MultiBinding> 
      </DataGridTextColumn.Binding> 
     </DataGridTextColumn> 
    </DataGrid.Columns> 
</DataGrid> 

某種類型的轉換器

class ItemsDifferenceConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, 
         object parameter, CultureInfo culture) 
    { 
     if (values.Length != 2) 
      return null; 

     var item = values[0] as Measurement; 
     var collection = values[1] as IEnumerable<Measurement>; 
     if (item == null || collection == null) 
      return null; 

     var list = collection.OrderBy(v => v.Date).ToList(); //it will be easier to find a previous date 
     var itemIndex = list.IndexOf(item); 
     if (itemIndex == 0) //First item 
      return null; 

     var diff = item.ValueGas - list[itemIndex - 1].ValueGas; 
     return (diff > 0 ? "+" : "") + diff.ToString(); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new Exception("The method or operation is not implemented."); 
    } 
} 

,但這例子並沒有與刪除/更新底層集合的項目工作。在這種情況下,中間ViewModel是最佳選擇。

這是我的方式來做到這一點。它適用於更新,刪除和添加項目。

/// <summary> 
/// Main ViewModel, contains items for DataGrid 
/// </summary> 
public class MeasurementListViewModel 
{ 
    public MeasurementListViewModel(IEnumerable<Measurement> measurements) 
    { 
     this.Items = new ObservableCollection<MeasurementViewModel>(measurements.Select(m=>new MeasurementViewModel(m))); 
     this.Measurements = (ListCollectionView)CollectionViewSource.GetDefaultView(this.Items); 

     this.Items.CollectionChanged += new NotifyCollectionChangedEventHandler(Items_CollectionChanged); 
     foreach(var m in this.Items) 
      m.PropertyChanged += new PropertyChangedEventHandler(Item_PropertyChanged); 

    } 

    //Date or Value were changed 
    void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     //Update the collection view if refresh isn't possible 
     if (this.Measurements.IsEditingItem) 
      this.Measurements.CommitEdit(); 
     if (this.Measurements.IsAddingNew) 
      this.Measurements.CommitNew(); 

     this.Measurements.Refresh(); 
    } 

    //Items were added or removed 
    void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     //Attach the observer for the properties 
     if (e.NewItems != null) 
      foreach (var vm in e.NewItems.OfType<MeasurementViewModel>()) 
       vm.PropertyChanged += Item_PropertyChanged; 

     //Refresh when it is possible 
     if(!this.Measurements.IsAddingNew && !this.Measurements.IsEditingItem) 
      this.Measurements.Refresh(); 
    } 

    private ObservableCollection<MeasurementViewModel> Items { get; set; } 

    public ListCollectionView Measurements { get; set; } 
} 

/// <summary> 
/// Wraps Measurement class and provide notification of changes 
/// </summary> 
public class MeasurementViewModel 
{ 
    public MeasurementViewModel() 
    { 
     this.Model = new Measurement(); 
    } 

    public MeasurementViewModel(Measurement m) 
    { 
     this.Model = m; 
    } 

    public Measurement Model { get; private set; } 

    public DateTime Date 
    { 
     get { return this.Model.Date; } 
     set 
     { 
      this.Model.Date = value; 
      OnPropertyChanged("Date"); 
     } 
    } 

    public double ValueGas 
    { 
     get { return this.Model.ValueGas; } 
     set 
     { 
      this.Model.ValueGas = value; 
      OnPropertyChanged("ValueGas"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     if (this.PropertyChanged != null) 
     { 
      this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

轉換器是一個有點不同:

class ItemsDifferenceConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, 
         object parameter, CultureInfo culture) 
    { 
     var item = values[0] as MeasurementViewModel; 
     var view = values[1] as ICollectionView; 
     if (item == null || view == null) 
      return null; 

     var list = view.SourceCollection.OfType<MeasurementViewModel>().OrderBy(v => v.Date).ToList(); //it will be easier to find a previous date 
     var itemIndex = list.IndexOf(item); 

     if (itemIndex == 0) //First item 
      return null; 

     var diff = item.ValueGas - list[itemIndex - 1].ValueGas; 
     return (diff > 0 ? "+" : "") + diff.ToString(); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new Exception("The method or operation is not implemented."); 
    } 
} 

而且DataGrid

<DataGrid ItemsSource="{Binding Path=Measurements}" AutoGenerateColumns="False" 
      CanUserAddRows="True"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" /> 
     <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" /> 
     <DataGridTextColumn Header="Difference"> 
      <DataGridTextColumn.Binding> 
       <MultiBinding Converter="{StaticResource ItemsDifferenceConverter}" Mode="OneWay"> 
        <Binding Path="."/> 
        <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="ItemsSource"/> 
       </MultiBinding> 
      </DataGridTextColumn.Binding> 
     </DataGridTextColumn> 
    </DataGrid.Columns> 
</DataGrid> 
+0

非常感謝您!這就像一個魅力:)但正如你所說,有一些缺點。但沒問題,這是一個小型解決方案! 只是爲了興趣:在這種情況下如何實現中間ViewModel? – khlr 2011-02-15 18:14:25

相關問題