简体   繁体   中英

DataTrigger When Greater Than a Number

I have a value that is caled OperativeCount . I would like the colour of the DataGridColumn to change when this number is greater than 10. Something similar to this;

<DataGrid.Resources>
    <Style x:Key="DGCellStyle" TargetType="DataGridCell">
        <Style.Triggers>
            <DataTrigger Binding="{Binding OperativeCount}" Value=">10">
                <Setter Property="FontWeight" Value="Bold"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.Resources>

Obviously for now the Value=">10" is not working but essentially that is what I would like to do.

Blend SDK for WPF can get it done very quickly without any code behind. Check out DataTrigger (Blend SDK for WPF) . Use ChangePropertyAction as the behavior.

<ei:DataTrigger Binding="{Binding OperativeCount}" Comparison="GreaterThan" Value="10">
  <ei:ChangePropertyAction PropertyName="FontWeight" >
     <ei:ChangePropertyAction.Value>
       <FontWeight>Bold</FontWeight>
     </ei:ChangePropertyAction.Value>
   </ei:ChangePropertyAction>
</ei:DataTrigger>

Don't bother much, let Blend take care of it.

For that you need a value converter. Here's how you might write that if you were me:

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace EdPlunkett
{
    public class GreaterThan : MarkupExtension, IValueConverter
    {
        //  The only public constructor is one that requires a double argument.
        //  Because of that, the XAML editor will put a blue squiggly on it if 
        //  the argument is missing in the XAML. 
        public GreaterThan(double opnd)
        {
            Operand = opnd;
        }

        /// <summary>
        /// Converter returns true if value is greater than this.
        /// 
        /// Don't let this be public, because it's required to be initialized 
        /// via the constructor. 
        /// </summary>
        protected double Operand { get; set; }

        //  When the XAML is parsed, each markup extension is instantiated 
        //  and the parser asks it to provide its value. Here, the value is 
        //  us. 
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return System.Convert.ToDouble(value) > Operand;
        }

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

And here's how you would use it:

<Window
    ....
    xmlns:edp="clr-namespace:EdPlunkett"
    ....
    >

    <DataGrid.Resources>
        <Style x:Key="DGCellStyle" TargetType="DataGridCell">
            <Style.Triggers>
                <DataTrigger 
                    Binding="{Binding OperativeCount, Converter={edp:GreaterThan 10}}" 
                    Value="True">
                    <Setter Property="FontWeight" Value="Bold" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Resources>

Value converters are very often instantiated as resources, and parameters for them are usually passed in via the ConverterParameter property, but if you make it a MarkupExtension , that lets the XAML parser enforce the type of the argument, as well as require the argument to be present. This makes the guts of the converter exceedingly simple. Plus you get Intellisense on it.

If you don't need to reuse this component or make it "general", a simpler and most focused solution can be the following.

Create a converter with this code:

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

namespace WpfApplication1
{
    public class CountToFontWeightConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return DependencyProperty.UnsetValue;

            var count = (int)value;

            if (count > 10)
                return FontWeights.Bold;
            else
                return FontWeights.Normal;
        }

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

And then use it this way:

<DataGrid.Resources>

  <local:CountToFontWeightConverter x:Key="CountToFontWeightConverter"/>

  <Style TargetType="{x:Type DataGridCell}" x:Key="DGCellStyle">
    <Setter Property="FontWeight"
            Value="{Binding OperativeCount,
              Converter={StaticResource CountToFontWeightConverter}}"/>
  </Style>

</DataGrid.Resources>

Obviously, if the OperativeCount property changes during your app's lifetime, it must raise notifications on change, via INotifyPropertyChanged implementation, or via Reactive library.

You can generalize a little bit this solution by passing the limit of 10 as a parameter for the converter, instead of hard-coding it inside the converter itself, so you can use it in several places with different limit.

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