This example demonstrates how to highlight grid cells that were edited by a user.
This approach does not highlight cells if you change their values at the data source level. In this case, you can use the DataUpdateFormatCondition instead.
Implementation Details
This example creates invisible unbound columns for each grid column. These Boolean unbound columns contain information on whether the corresponding grid column's cell was modified by a user. The CustomUnboundColumnData event populates unbound columns with data. The specified format condition highlights cells based on data in unbound columns.
Files to Review
- HighlightChangedCellBehavior.cs (VB: HighlightChangedCellBehavior.vb)
- ViewModel.cs (VB: ViewModel.vb)
- Window1.xaml (VB: Window1.xaml)
Documentation
More Examples
- WPF MVVM Behaviors - Create a Custom Attached Behavior
- WPF Data Grid - Apply Conditional Formatting in Code Behind
- WPF Data Grid - Create Unbound Columns
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
C#using DevExpress.Mvvm.UI.Interactivity;
using DevExpress.Xpf.Grid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace HighlightModifiedCells {
public class ChangedCellsHighlightBehavior : Behavior<GridControl> {
const string UnboundColumnPrefix = "IsEdited_";
Dictionary<Tuple<object, string>, bool> modifiedCells = new Dictionary<Tuple<object, string>, bool>();
Dictionary<Tuple<object, string>, object> originalValues = new Dictionary<Tuple<object, string>, object>();
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.Register("HighlightBrush", typeof(Brush), typeof(ChangedCellsHighlightBehavior));
public Brush HighlightBrush {
get { return (Brush)GetValue(HighlightBrushProperty); }
set { SetValue(HighlightBrushProperty, value); }
}
protected TableView View {
get { return (TableView)AssociatedObject.View; }
}
protected override void OnAttached() {
base.OnAttached();
AssociatedObject.CustomUnboundColumnData += OnGridCustomUnboundColumnData;
AssociatedObject.Loaded += OnGridLoaded;
}
protected override void OnDetaching() {
AssociatedObject.CustomUnboundColumnData -= OnGridCustomUnboundColumnData;
View.CellValueChanged -= OnViewCellValueChanged;
base.OnDetaching();
}
void OnGridLoaded(object sender, System.Windows.RoutedEventArgs e) {
AssociatedObject.Loaded -= OnGridLoaded;
View.CellValueChanged += OnViewCellValueChanged;
CreateColumnsAndConditions();
}
void OnViewCellValueChanged(object sender, CellValueChangedEventArgs e) {
if (!IsServiceColumn(e.Column)) {
var fieldName = $"{UnboundColumnPrefix}{e.Column.FieldName}";
var key = Tuple.Create(e.Row, fieldName);
object originalValue;
bool isModified = true;
if (originalValues.TryGetValue(key, out originalValue))
isModified = !Equals(originalValue, e.Value);
else
originalValues[key] = e.OldValue;
AssociatedObject.SetCellValue(e.RowHandle, fieldName, isModified);
}
}
void OnGridCustomUnboundColumnData(object sender, GridColumnDataEventArgs e) {
if (IsServiceColumn(e.Column)) {
var key = Tuple.Create(AssociatedObject.GetRowByListIndex(e.ListSourceRowIndex), e.Column.FieldName);
if (e.IsGetData) {
bool res;
e.Value = modifiedCells.TryGetValue(key, out res) ? res : false;
}
if (e.IsSetData)
modifiedCells[key] = (bool)e.Value;
}
}
static bool IsServiceColumn(GridColumn column) {
return column.FieldName.StartsWith(UnboundColumnPrefix);
}
void CreateColumnsAndConditions() {
foreach (GridColumn column in AssociatedObject.Columns.ToList()) {
GridColumn unboundColumn = new GridColumn();
unboundColumn.FieldName = UnboundColumnPrefix + column.FieldName;
unboundColumn.UnboundDataType = typeof(bool);
unboundColumn.Visible = false;
unboundColumn.ShowInColumnChooser = false;
AssociatedObject.Columns.Add(unboundColumn);
View.FormatConditions.Add(new FormatCondition() {
FieldName = column.FieldName,
Expression = string.Format("[{0}] = true", unboundColumn.FieldName),
Format = new DevExpress.Xpf.Core.ConditionalFormatting.Format() { Background = HighlightBrush }
});
}
}
}
}
C#using System;
using System.Collections.ObjectModel;
namespace HighlightModifiedCells {
public class ViewModel {
public ObservableCollection<Customer> Customers { get; set; }
public ViewModel() {
Customers = new ObservableCollection<Customer>();
for (int i = 1; i < 30; i++) {
Customers.Add(new Customer() { ID = i, Name = "Customer" + i, RegistrationDate = DateTime.Now.AddDays(i) });
}
}
}
public class Customer {
public int ID { get; set; }
public string Name { get; set; }
public DateTime RegistrationDate { get; set; }
}
}
XAML<Window x:Class="HighlightModifiedCells.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
xmlns:local="clr-namespace:HighlightModifiedCells"
Height="500" Width="400">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<DockPanel>
<dxg:GridControl ItemsSource="{Binding Customers}" AutoGenerateColumns="AddNew">
<dxmvvm:Interaction.Behaviors>
<local:ChangedCellsHighlightBehavior HighlightBrush="LightPink"/>
</dxmvvm:Interaction.Behaviors>
<dxg:GridControl.View>
<dxg:TableView AutoWidth="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
</DockPanel>
</Window>