0

I have a datagrid, which is bound to a datatable, each cell has a unique value, so no two cells have the same value.

I want to change the cell with the value 1 (int32) to the color green. Note, the value of 1 is dynamic, thats just an example, it could be between 1-90.

I have searched around, and most of the help gives you the value of either a cell based on its coordinates i.e. (4,2) or the selected cell. This isn't what I want, I want to change the color of a cell based on its value.

Is there a way to do this, for example in JavaScript i would simply assign each cell an id equivalent to its value and then something like $('#' + 1).css('background-color:green;') (note: this might not be correct syntax, but you get the picture). Is there a way as simple as this or a standard way of doing this?

My datagrid

<DataGrid Name="grid" ItemsSource="{Binding}" Height="300" Width="900"
          AutoGenerateColumns="True"
          VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Center" VerticalAlignment="Top" RowHeight="40">
            <DataGrid.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </DataGrid.ItemsPanel>
        </DataGrid>

Table creation

DataSet dataSet = new DataSet("myDS");
            DataTable numbersTable = new DataTable("Numbers");
            numbersTable.Columns.Add("Number", typeof(Int32));
            for (int i = 1; i < 91; i++)
            {
                numbersTable.Rows.Add(i);
            }
            dataSet.Tables.Add(numbersTable);
            grid.DataContext = numbersTable.DefaultView;
RSM
  • 14,540
  • 34
  • 97
  • 144
  • Have you tried finding the desired value by looping through each (foreach) row of datagrid? I mean find the coordinates of the cell with desired value, and then apply style or whatever... Just a suggestion. – Asad Malik Nov 26 '13 at 22:23
  • Could you put this as an answer and ill see if it helps? – RSM Nov 28 '13 at 10:50

3 Answers3

3

Please take a look on this Change DataGrid cell colour based on values

Public class NameToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string input = value as string;
        switch (input)
        {
            case "John":
                return Brushes.LightGreen;
            default:
                return DependencyProperty.UnsetValue;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Grid:

<DataGridTextColumn Binding="{Binding Name}">
    <DataGridTextColumn.ElementStyle>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Background" Value="{Binding Name, Converter={StaticResource NameToBrushConverter}}"/>
        </Style>
    </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

Brushs:

public string Name
{
    get { return _name; }
    set
    {
        if (_name != value)
        {
            _name = value;
            OnPropertyChanged("Name");
            OnPropertyChanged("NameBrush");
        }
    }
}

public Brush NameBrush
{
    get
    {
        switch (Name)
        {
            case "John":
                return Brushes.LightGreen;
        }

        return Brushes.Transparent;
    }
}
Community
  • 1
  • 1
Mohamed Rozza
  • 567
  • 8
  • 12
1

There are a few ways that you can achieve your requirements. It's a little bit inconvenient because we have to apply a Style with a Trigger on the columns of the DataGrid rather than the DataGrid itself. This means that you can't use the AutoGenerateColumns feature and you'll have to define them all manually as I have below. Try this:

<DataGrid ItemsSource="{Binding YourItems}" AutoGenerateColumns="False">
    <DataGrid.Resources>
        <Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
            <Style.Triggers>
                <Trigger Property="Text" Value="1">
                    <Setter Property="Background" Value="LightGreen" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding YourPropertyName}" 
            ElementStyle="{StaticResource BackgroundColourStyle}">
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

UPDATE >>>

Ok, so to do this with a variable value, you'd be better off doing this the WPF data-centric way. As usual in WPF, we want to create data objects with all of the properties that we need to display in the UI. As such, you'd need to add a new bool property into whatever data type class that you are displaying in the DataGrid... maybe add a new column for that if you insist on using a DataTable.

However, I'd advise you to use a class and if you create one, you must ensure that you implement the INotifyPropertyChanged interface correctly in it. You could add properties into it like this:

public int NumberValue { get; set; } // implement `INotifyPropertyChanged` here
public bool HasHighlightValue { get; set; } // implement `INotifyPropertyChanged` here

Then we could use this property to highlight the relevant cell in the DataGrid:

<Style x:Key="BackgroundColourStyle" TargetType="{x:Type TextBlock}">
    <Style.Triggers>
        <Trigger Property="HasHighlightValue" Value="True">
            <Setter Property="Background" Value="LightGreen" />
        </Trigger>
    </Style.Triggers>
</Style>

Finally, you can set this new property in a handler or code behind in response to some user action:

// reset previous cell value
YourDataType previousItem = YourItems.Where(I => i.HasHighlightValue).Single();
previousItem.HasHighlightValue = false;
// set new cell value
YourDataType item = YourItems.Where(I => i.NumberValue == relevantNumber).Single();
item.HasHighlightValue = true;
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Im looking for a dynamic solution, and the value was just an example, it could be any value – RSM Nov 22 '13 at 14:48
  • What does *dynamic solution* mean? – Sheridan Nov 22 '13 at 14:53
  • something that works for any value I require of which may be undetermined to myself. – RSM Nov 25 '13 at 10:20
  • instead of using code behind like this, wouldn't it be better to use a converter + DataTrigger combination instead? – Maverik Nov 25 '13 at 12:17
  • 1
    @franssu, correct me if I'm wrong, but you *seem* to be suggesting that I copied my answer from the question that you linked to solely on the basis of the `Color` that I was using in my test project. Personally, I find that quite insulting... as if I couldn't come up with a `Trigger` without copying it. If that was your point, then thanks... great contribution. If that wasn't your point, what was? – Sheridan Nov 25 '13 at 12:20
  • @Maverik, you're quite welcome to add your own answer demonstrating your point. – Sheridan Nov 25 '13 at 12:21
  • @Sheridan Please accept my apologies. Still, this thread might interest OP : http://stackoverflow.com/questions/5549617/change-datagrid-cell-colour-based-on-values – franssu Nov 25 '13 at 13:26
  • @Sheridan it was a question just to know if it would be better. You've done most of the work already and I don't think its fair me copying most of it just to show a different point of view, especially since this is a bounty question. You deserve the bounty for a full fledge answer. – Maverik Nov 27 '13 at 09:19
  • @Maverik, the code that I showed would work just as well in a view model... it doesn't have to be in code behind. – Sheridan Nov 27 '13 at 09:21
  • Hi, will test this theory today. Will let you know. Thanks. – RSM Nov 28 '13 at 10:52
  • Btw just gave you the points cos you tried to help most, havent really got a clue what any of these answers are talking about. – RSM Dec 03 '13 at 16:27
1

There is also following way which you can use to dynamically format DataGridCell whenever its value changes by using Binding.TargetUpdated or Binding.SourceUpdated event on DataGridCell.

To do this, you must do following:

  • Add event handler for AutoGeneratingColumn event on DataGrid. For example:

<DataGrid ItemsSource="{Binding}" AutoGeneratingColumn="OnAutoGeneratingColumn"/>

  • In AutoGeneratingColumn handler identify if auto-generated column has Binding and if so, set NotifyOnTargetUpdated on it to true and set CellStyle on column which will include EventSetter for Binding.TargetUpdatedEvent event. For example:

    void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        BindingBase bindingBase = null;
    
        var dataGridBoundColumn = e.Column as DataGridBoundColumn;
        if (dataGridBoundColumn != null)
        {
            bindingBase = dataGridBoundColumn.Binding;
        }
        else
        {
            var dataGridComboBoxColumn = e.Column as DataGridComboBoxColumn;
            if (dataGridComboBoxColumn != null)
                bindingBase = dataGridComboBoxColumn.SelectedItemBinding;
        }
    
        var binding = bindingBase as Binding;
        if (binding != null)
        {
            binding.NotifyOnTargetUpdated = true;
    
            e.Column.CellStyle = new Style(typeof(DataGridCell))
            {
                Setters = 
                {
                    new EventSetter(Binding.TargetUpdatedEvent, new EventHandler<DataTransferEventArgs>(OnDataGridCellBindingTargetUpdated))
                }
            };
        }
    }
    
  • Implement your custom logic for formatting DataGridCell when value changes in OnDataGridCellBindingTargetUpdated handler. For example:

    private static void OnDataGridCellBindingTargetUpdated(object sender, DataTransferEventArgs e)
    {
        var dataGridCell = (DataGridCell)sender;
    
        // Get context: column and item.
        var column = dataGridCell.Column;
        var item = dataGridCell.DataContext;
    
        // TODO: based on context, format DataGridCell instance.
    }
    
Stipo
  • 4,566
  • 1
  • 21
  • 37