Extreme snap? No problems.
<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>
Some kind of converter
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();
But this example does not work with deleting / updating elements of the base collection. In this case, an intermediate ViewModel would be the best choice.
Here is my way to do this. It works with updating, deleting and adding items.
/// <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)); } } }
The converter is a little different:
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();
And 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>