在我的小应用程序中,我有一个DataGrid(见截图),它绑定到一个Measurement对象列表。 Measurement只是一个具有两个属性的数据容器:Date和CounterGas(float)。 每个Measurement对象代表特定日期的燃气消耗量。
测量列表绑定到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>
好吧,现在我的问题:) 我想在“Counter Gas”列旁边有另一个列,它显示实际计数器值和最后一个计数器值之间的差异。
E.g。这个额外的列应该计算2月13日和2月6日之间的差值=&gt; 199.789 - 187.115 = 15.674
实现这一目标的最佳方法是什么?我想避免在Measurement类中进行任何只能保存数据的计算。我更喜欢DataGrid来处理计算。 那么有没有办法添加另一个只计算值之间差异的列?也许使用某种转换器和极端绑定? ; d
P.S。:也许名声较好的人可以嵌入屏幕截图。谢谢:))
答案 0 :(得分: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>