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