Change DataGrid cell colour based on values

前端 未结 8 1475
栀梦
栀梦 2020-11-22 08:48

I have got a WPF datagrid and I want diffrent cell colours according to values. I have got below code on my xaml

Style TargetType=\"DataGridCell\"

相关标签:
8条回答
  • 2020-11-22 09:08

    If you need to do it with a set number of columns, H.B.'s way is best. But if you don't know how many columns you are dealing with until runtime, then the below code [read: hack] will work. I am not sure if there is a better solution with an unknown number of columns. It took me two days working at it off and on to get it, so I'm sticking with it regardless.

    C#

    public class ValueToBrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            int input;
            try
            {
                DataGridCell dgc = (DataGridCell)value;
                System.Data.DataRowView rowView = (System.Data.DataRowView)dgc.DataContext;
                input = (int)rowView.Row.ItemArray[dgc.Column.DisplayIndex];
            }
            catch (InvalidCastException e)
            {
                return DependencyProperty.UnsetValue;
            }
            switch (input)
            {
                case 1: return Brushes.Red;
                case 2: return Brushes.White;
                case 3: return Brushes.Blue;
                default: return DependencyProperty.UnsetValue;
            }
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    XAML

    <UserControl.Resources>
        <conv:ValueToBrushConverter x:Key="ValueToBrushConverter"/>
        <Style x:Key="CellStyle" TargetType="DataGridCell">
            <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ValueToBrushConverter}}" />
        </Style>
    </UserControl.Resources>
    <DataGrid x:Name="dataGrid" CellStyle="{StaticResource CellStyle}">
    </DataGrid>
    
    0 讨论(0)
  • 2020-11-22 09:09

    If you try to set the DataGrid.CellStyle the DataContext will be the row, so if you want to change the colour based on one cell it might be easiest to do so in specific columns, especially since columns can have varying contents, like TextBlocks, ComboBoxes and CheckBoxes. Here is an example of setting all the cells light-green where the Name is John:

    <DataGridTextColumn Binding="{Binding Name}">
        <DataGridTextColumn.ElementStyle>
            <Style TargetType="{x:Type TextBlock}">
                <Style.Triggers>
                    <Trigger Property="Text" Value="John">
                        <Setter Property="Background" Value="LightGreen"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </DataGridTextColumn.ElementStyle>
    </DataGridTextColumn>
    

    A Screenshot


    You could also use a ValueConverter to change the colour.

    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();
        }
    }
    

    Usage:

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

    Yet another option is to directly bind the Background to a property which returns the respectively coloured brush. You will have to fire property change notifications in the setters of properties on which the colour is dependent.

    e.g.

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
                OnPropertyChanged(nameof(NameBrush));
            }
        }
    }
    
    public Brush NameBrush
    {
        get
        {
            switch (Name)
            {
                case "John":
                    return Brushes.LightGreen;
                default:
                    break;
            }
    
            return Brushes.Transparent;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 09:20

    In my case convertor must return string value. I don't why, but it works.

    *.xaml (common style file, which is included in another xaml files)

    <Style TargetType="DataGridCell">
            <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ValueToBrushConverter}}" />
    </Style>
    

    *.cs

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Color color = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey);
        return "#" + color.Name;
    }
    
    0 讨论(0)
  • 2020-11-22 09:23

    This may be of help to you. It isn't the stock WPF datagrid however.

    I used DevExpress with a custom ColorFormatter behaviour. I couldn't find anything on the market that did this out of the box. This took me a few days to develop. My code attaached below, hopefully this helps someone out there.

    Edit: I used POCO view models and MVVM however you could change this to not use POCO if you desire.

    Viewmodel.cs

    namespace ViewModel
    {
        [POCOViewModel]
        public class Table2DViewModel
        {
            public ITable2DView Table2DView { get; set; }
    
            public DataTable ItemsTable { get; set; }
    
    
            public Table2DViewModel()
            {
            }
    
            public Table2DViewModel(MainViewModel mainViewModel, ITable2DView table2DView) : base(mainViewModel)
            {
                Table2DView = table2DView;   
                CreateTable();
            }
    
            private void CreateTable()
            {
                var dt = new DataTable();
                var xAxisStrings = new string[]{"X1","X2","X3"};
                var yAxisStrings = new string[]{"Y1","Y2","Y3"};
    
                //TODO determine your min, max number for your colours
                var minValue = 0;
                var maxValue = 100;
                Table2DView.SetColorFormatter(minValue,maxValue, null);
    
                //Add the columns
                dt.Columns.Add(" ", typeof(string));
                foreach (var x in xAxisStrings) dt.Columns.Add(x, typeof(double));
    
                //Add all the values
                double z = 0;
                for (var y = 0; y < yAxisStrings.Length; y++)
                {
                    var dr = dt.NewRow();
                    dr[" "] = yAxisStrings[y];
                    for (var x = 0; x < xAxisStrings.Length; x++)
                    {
                        //TODO put your actual values here!
                        dr[xAxisStrings[x]] = z++; //Add a random values
                    }
                    dt.Rows.Add(dr);
                }
                ItemsTable = dt;
            }
    
    
            public static Table2DViewModel Create(MainViewModel mainViewModel, ITable2DView table2DView)
            {
                var factory = ViewModelSource.Factory((MainViewModel mainVm, ITable2DView view) => new Table2DViewModel(mainVm, view));
                return factory(mainViewModel, table2DView);
            }
        }
    
    }
    

    IView.cs

    namespace Interfaces
        {
            public interface ITable2DView
            {
                void SetColorFormatter(float minValue, float maxValue, ColorScaleFormat colorScaleFormat);
            }
        }
    

    View.xaml.cs

    namespace View
    {
        public partial class Table2DView : ITable2DView
        {
            public Table2DView()
            {
                InitializeComponent();
            }
    
            static ColorScaleFormat defaultColorScaleFormat = new ColorScaleFormat
            {
                ColorMin = (Color)ColorConverter.ConvertFromString("#FFF8696B"),
                ColorMiddle = (Color)ColorConverter.ConvertFromString("#FFFFEB84"),
                ColorMax = (Color)ColorConverter.ConvertFromString("#FF63BE7B")
            };
    
            public void SetColorFormatter(float minValue, float maxValue, ColorScaleFormat colorScaleFormat = null)
            {
                if (colorScaleFormat == null) colorScaleFormat = defaultColorScaleFormat;
                ConditionBehavior.MinValue = minValue;
                ConditionBehavior.MaxValue = maxValue;
                ConditionBehavior.ColorScaleFormat = colorScaleFormat;
            }
        }
    }
    

    DynamicConditionBehavior.cs

    namespace Behaviors
    {
        public class DynamicConditionBehavior : Behavior<GridControl>
        {
            GridControl Grid => AssociatedObject;
    
            protected override void OnAttached()
            {
                base.OnAttached();
                Grid.ItemsSourceChanged += OnItemsSourceChanged;
            }
    
            protected override void OnDetaching()
            {
                Grid.ItemsSourceChanged -= OnItemsSourceChanged;
                base.OnDetaching();
            }
    
            public ColorScaleFormat ColorScaleFormat { get; set;}
            public float MinValue { get; set; }
            public float MaxValue { get; set; }
    
            private void OnItemsSourceChanged(object sender, EventArgs e)
            {
                var view = Grid.View as TableView;
    
                if (view == null) return;
    
                view.FormatConditions.Clear();
    
                foreach (var col in Grid.Columns)
                {
                    view.FormatConditions.Add(new ColorScaleFormatCondition
                    {
                        MinValue = MinValue,
                        MaxValue = MaxValue,
                        FieldName = col.FieldName,
                        Format = ColorScaleFormat,
                    });
                }
    
            }
        }
    }
    

    View.xaml

    <UserControl x:Class="View"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" 
                 xmlns:ViewModels="clr-namespace:ViewModel"
                 xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
                 xmlns:behaviors="clr-namespace:Behaviors"
                 xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking"
                 DataContext="{dxmvvm:ViewModelSource Type={x:Type ViewModels:ViewModel}}"
                 mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="800">
    
        <UserControl.Resources>
            <Style TargetType="{x:Type dxg:GridColumn}">
                <Setter Property="Width" Value="50"/>
                <Setter Property="HorizontalHeaderContentAlignment" Value="Center"/>
            </Style>
    
            <Style TargetType="{x:Type dxg:HeaderItemsControl}">
                <Setter Property="FontWeight" Value="DemiBold"/>
            </Style>
        </UserControl.Resources>
    
            <!--<dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="" Command="{Binding OnLoadedCommand}"/>
            </dxmvvm:Interaction.Behaviors>-->
            <dxg:GridControl ItemsSource="{Binding ItemsTable}"
                         AutoGenerateColumns="AddNew"
                         EnableSmartColumnsGeneration="True">
    
            <dxmvvm:Interaction.Behaviors >
                <behaviors:DynamicConditionBehavior x:Name="ConditionBehavior" />
                </dxmvvm:Interaction.Behaviors>
                <dxg:GridControl.View>
                    <dxg:TableView ShowGroupPanel="False"
                               AllowPerPixelScrolling="True"/>
                </dxg:GridControl.View>
            </dxg:GridControl>
      </UserControl>
    
    0 讨论(0)
  • 2020-11-22 09:23

    To do this in the Code Behind (VB.NET)

    Dim txtCol As New DataGridTextColumn
    
    Dim style As New Style(GetType(TextBlock))
    Dim tri As New Trigger With {.Property = TextBlock.TextProperty, .Value = "John"}
    tri.Setters.Add(New Setter With {.Property = TextBlock.BackgroundProperty, .Value = Brushes.Green})
    style.Triggers.Add(tri)
    
    xtCol.ElementStyle = style
    
    0 讨论(0)
  • 2020-11-22 09:28

    Based on the answer by 'Cassio Borghi'. With this method, there is no need to change the XAML at all.

            DataGridTextColumn colNameStatus2 = new DataGridTextColumn();
            colNameStatus2.Header = "Status";
            colNameStatus2.MinWidth = 100;
            colNameStatus2.Binding = new Binding("Status");
            grdComputer_Servives.Columns.Add(colNameStatus2);
    
            Style style = new Style(typeof(TextBlock));
            Trigger running = new Trigger() { Property = TextBlock.TextProperty, Value = "Running" };
            Trigger stopped = new Trigger() { Property = TextBlock.TextProperty, Value = "Stopped" };
    
            stopped.Setters.Add(new Setter() { Property = TextBlock.BackgroundProperty, Value = Brushes.Blue });
            running.Setters.Add(new Setter() { Property = TextBlock.BackgroundProperty, Value = Brushes.Green });
    
            style.Triggers.Add(running);
            style.Triggers.Add(stopped);
    
            colNameStatus2.ElementStyle = style;
    
            foreach (var Service in computerResult)
            {
                var RowName = Service;  
                grdComputer_Servives.Items.Add(RowName);
            }
    
    0 讨论(0)
提交回复
热议问题