简体   繁体   中英

Change WPF DataGrid rows background color using CheckBox MVVM

I have converter for applying background color for DataGrid rows based on values in one column. It is working fine and is applying background color "on load". However I decided to dive deeper into WPF and MVVM and try to attach this event to CheckBox, but failed at this point. I am not getting any errors but my CheckBox does not work either. Any suggestions what should be edited? I guess I need to attach Convert event somehow to BacgroundColorBool ?

IDToBackgroundConverter.cs:

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace Inspector_FilterTest
{
    class IDToBackgroundConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is string)
            {
                if (value.ToString().Trim().StartsWith("7")) return Brushes.DarkSlateBlue;
                if (value.ToString().Trim().StartsWith("6")) return Brushes.Peru;
            }
            // value is not an integer. Do not throw an exception
            // in the converter, but return something that is obviously wrong
            return Brushes.Firebrick;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }
}

I use it in XAML:

<Window.Resources>

        <Inspector_FilterTest:IDToBackgroundConverter x:Key="IDToBackgroundConverter"/>

</Window.Resources>

...

        <DataGrid x:Name="DataGrid1" ItemsSource="{Binding MainDataTable}">
            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow" >
                    <Setter Property="Background" Value="{Binding YRNRO, Converter={StaticResource IDToBackgroundConverter}}" />
                </Style>
            </DataGrid.RowStyle>
         </DataGrid>

This is working fine and background colors are set (based on starting number in YRNRO) when I am loading my DataGrid. However I would like to be able to control this with CheckBox.

I have created a CheckBox:

ViewModel_Main.cs:

    // Binding checkbox background color
    private bool _BacgroundColorBool;
    public bool BacgroundColorBool
    {
        get => this._BacgroundColorBool;
        set
        {
            this._BacgroundColorBool = value;
            OnPropertyChanged();

            // Refresh the DataTable filter expression
            EnableRowFiltering();
        }
    }

XAML:

    <CheckBox Style="{StaticResource MyCheckBox}" IsChecked="{Binding BacgroundColorBool}" x:Name="ActiveCustomer_Copy" Content="" HorizontalAlignment="Left" Margin="221,55,0,0" VerticalAlignment="Top"/>

but I don't understand how to connect this all together in order to be able to control "background fill applied"/"background fill not applied" with CheckBox.


EDIT:

Some corrections done to code originally provided by BionicCode (no errors in debugger anymore, I hope this is correct?). In order to get color back to transparent in Datagrid after CheckBox is unchecked pay attention to return Brushes.Transparent; . That is where you can apply "revertBack" logic.

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace Liinos_inspector_FilterTest
{
    class IDToBackgroundConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            // The order of the parameters in the 'values' array is equal to the order 
            // of the input binding as they are defined in the MultiBinding

            var isBackgroundEnabled = (bool)values[0];
            if (!isBackgroundEnabled)
            {
                return Brushes.Transparent;
            }

            if (values[1] is string stringValue)
            {
                return stringValue.Trim().StartsWith("7") 
                ? Brushes.DarkSlateBlue
                : stringValue.Trim().StartsWith("6") 
                  ? Brushes.Peru
                  : Brushes.Firebrick;
            }

            return Binding.DoNothing;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

One solution, that doesn't require modification of your view model, is to use a MultiBinding with a IMultiValueConverter . A IMultiValueConverter accepts multiple inputs returned from a MultiBinding , to convert them to a single output (and reverse):

First, turn the IValueConverter into a IMultiValueConverter .
It's best practice to throw the exception that describes the cause of the error the best.
The NotImplementedException is primarily intended to notify the developer that he forgot to implement a relevant and referenced method. It's like a 'TODO'. Since you deliberately decided not to implement the ConvertBack member, because you decided not to support the backwards conversion, you should throw a NotSupportedException instead. This notifies the consumer of your type that the method is not supported instead of implying that the author simply forgot to implement it.

class IDToBackgroundConverter : IMultiValueConverter
{      
  public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  { 
    // The order of the parameters in the 'values' array is equal to the order 
    // of the input binding as they are defined in the MultiBinding

    var isBackgroundEnabled = (bool) values[0];
    if (!isBackgroundEnabled)
    {
      return Brushes.Transparent;
    }
    
    if (values[1] is string stringValue)
    {
      return stringValue.Trim().StartsWith("7") 
        ? Brushes.DarkSlateBlue 
        : stringValue.Trim().StartsWith("6") 
          ? Brushes.Peru
          : Brushes.Firebrick;
    }

    return Binding.DoNothing;
  }

  // Throw a NotSupportedException instead of a NotImplementedException
  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    => throw new NotSupportedException();
}

Setup the MultiBinding :

<CheckBox IsChecked="{Binding BackgroundColorBool}" />

<DataGrid>
  <DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
      <Setter Property="Background">
        <Setter.Value>
          <MultiBinding>
            <MultiBinding.Converter>
              <IDToBackgroundConverter />
            </MultiBinding.Converter>

            <Binding Path="BackgroundColorBool" />
            <Binding Path="YRNRO" />
          </MultiBinding>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.RowStyle>
</DataGrid>

Because the purpose of the logic is to handle the coloring or visuals of the view, you should not implement this logic or store related data in the view model.
View and view model should never mix!

Whether to use the previous example or to eliminate the BackgroundColorBool property in the view model, depends on the the purpose of the CheckBox . Its name suggests a data related purpose ("ActiveCustomer...") and of course could bind to the view model.
But the name of the source property ("BackgroundColor..."), the CheckBox binds to, suggests a merely view related purpose. In this case the property should not be in the view model. Instead move it as a DependencyProperty to the hosting view or bind the CheckBox directly:

<CheckBox x:Name="ActiveCustomer_Copy" />

<DataGrid>
  <DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
      <Setter Property="Background">
        <Setter.Value>
          <MultiBinding>
            <MultiBinding.Converter>
              <IDToBackgroundConverter />
            </MultiBinding.Converter>

            <Binding ElementName="ActiveCustomer_Copy" 
                     Path="IsChecked" />
            <Binding Path="YRNRO" />
          </MultiBinding>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.RowStyle>
</DataGrid>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM