0
votes

I have a simple datagrid which has columns autogenerated and is bound to an item source. This item source is updated at some intervals and I can't find how to fire an event for a single cell changed. I want to change the color of the cell based on if the update to the data source changed the previous value of the cell.

I looked at Highlighting cells in WPF DataGrid when the bound value changes as well as http://codefornothing.wordpress.com/2009/01/25/the-wpf-datagrid-and-me/ but I am still unsure about how to go about implementing this. Some example code would be really helpful to get started on the right path.

1

1 Answers

1
votes

If you're binding to a DataTable, I don't think this would be a productive path to go down. Doing any kind of styling based on the contents of a DataTable bound DataGrid is nearly impossible in WPF. There are several suggestions on StackOverflow, but they are usually pretty hacky, event-driven (which is generally bad news in WPF), and a maintenance nightmare.

If however, the ItemsSource you are binding to is an ObservableCollection, where RowViewModel is the class that represents the data in a single row of the DataGrid, then it shouldn't be too bad. Make sure that RowViewModel implements INotifyPropertyChanged, and simply update the individual RowViewModels with their updated data. Then, you can add the logic to expose an additional property on your RowViewModel that indicates if a particular value is new - just use some styles/triggers in the XAML to set the background color based on the value of this new property.

Here's an example of the latter: If you edit one of values in the first column, it will turn the cell red. The same thing will happen if you change the value in your ItemViewModel programmatically via Database update.

The XAML:

<Window x:Class="ShowGridUpdates.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Item1, UpdateSourceTrigger=PropertyChanged}">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Style.Setters>
                            <Setter Property="Background" Value="Blue"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Style.Setters>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Item1Changed}" Value="True">
                                <Setter Property="Background" Value="Red"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
            <DataGridTextColumn Binding="{Binding Item2}"/>
        </DataGrid.Columns>
    </DataGrid>   
</Grid>

The Code-Behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }
}

public class ViewModel : PropertyChangedNotifier
{
    public ViewModel()
    {
        Items = new ObservableCollection<ItemViewModel>()
        {
            new ItemViewModel(){Item1="Item1FistValue", Item2="Item2FirstValue"},
            new ItemViewModel(){Item1="whocareswhatvalue", Item2="Icertainlydont"}
        };

        //just to get the initial state correct
        foreach (var item in Items)
        {
            item.Item1Changed = false;
        }
    }

    private ObservableCollection<ItemViewModel> _items;

    public ObservableCollection<ItemViewModel> Items
    {
        get
        {
            return _items;
        }
        set
        {
            _items = value;
            OnPropertyChanged("Items");
        }
    }
}

public class ItemViewModel : PropertyChangedNotifier
{
    private string _item1;
    private string _item2;

    private bool _item1Changed;

    public bool Item1Changed
    {
        get
        {
            return _item1Changed;
        }
        set
        {
            _item1Changed = value;
            OnPropertyChanged("Item1Changed");
        }
    }

    public string Item1
    {
        get
        {
            return _item1;
        }
        set
        {
            if (_item1 != value)
                Item1Changed = true;
            else
                Item1Changed = false;

            _item1 = value;
            OnPropertyChanged("Item1");
        }
    }

    public string Item2
    {
        get
        {
            return _item2;
        }
        set
        {
            _item2 = value;
            OnPropertyChanged("Item2");
        }
    }
}

public class PropertyChangedNotifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}