wpf-controlsdependency-propertiesdatatrigger

Dependency property for ItemsSource column to set DataTrigger condition


How would I properly bound to ItemsSource column via Dependency property of control (Datagrid), in order to set It's DataTrigger working?

My goal that works without dependency property:

<Style TargetType="DataGridRow">
   <Style.Triggers>
      <DataTrigger Binding="{Binding NAME}" Value="{x:Null}">
        <Setter Property="Visibility" Value="Collapsed"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

And this is how I want It to work:

<Style TargetType="DataGridRow">   
   <Style.Triggers>
      <DataTrigger Binding="{Binding HideRow, ElementName =_myGrid}" Value="{x:Null}">
         <Setter Property="Visibility" Value="Collapsed"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

My dependency property :

public static DependencyProperty HideRowProperty =
                        DependencyProperty.Register("HideRow", typeof(PersonsModel), typeof(My_DataGrid), 
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

///<summary>Property for hiding rows, based on column name</summary>
public PersonsModel HideRow
{
    get { return (PersonsModel)GetValue(HideRowProperty); }
    set { SetValue(HideRowProperty, value); }
}

And this is how I try to bind control in XAML:

<ctl:My_DataGrid ItemsSource="{Binding Persons}" HideRow="{Binding Persons.NAME}">

More explanation: ItemsSource Persons is ObservableCollection of PersonsModel (which is type of Dependency property).

I'm getting BindingExpression path error: 'NAME' property not found on 'object' ''ObservableCollection`1' error.


Solution

  • Based on your comments, your goal is to tell DataGrid to hide the row that has some property value == null. Hence, you have to assign the property's name to HideRow property and use a converter which uses reflection to get the property value and affect the style trigger.

    You have 2 options, the first one is simpler and you don't need the HideRow property at all:

    1. Change RowStyle a bit, Here you will pass the name of the property as a ConverterParameter (so it might be Name, Age, etc...)
    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ., Converter={StaticResource NullPropertyToBoolConverter}, ConverterParameter=Name}" Value="True">
                    <Setter Property="Visibility" Value="Collapsed" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
    

    Where NullPropertyToBoolConverter is

    public class NullPropertyToBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is PersonsModel person && parameter is string propertyName)
            {
                return typeof(PersonsModel).GetProperty(propertyName)?.GetValue(person, null) == null;
            }
            return value;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    The converter is defined in resources of one of DataGrid's parents (or in Application.xaml)

    <converters:NullPropertyToBoolConverter x:Key="NullPropertyToBoolConverter" />
    

    It is getting the value of Name, and return true if it was null, and this will cause the row visibility to be Collapsed.

    This option is suitable because in both options you have to define RowStyle but here you don't need to define a dependency property.

    1. Here you would change HideRow type to string instead of PersonsModel, and use it like this:
    <ctl:My_DataGrid ItemsSource="{Binding Persons}" HideRow="Name" >
    

    So, it might be Name, Age, etc...

    You'd define <DataGrid.RowStyle> similar to this

    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="Visibility">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource NullPropertyToBoolConverter2}">
                        <Binding Path="DataContext" RelativeSource="{RelativeSource Mode=Self}" />
                        <Binding Path="HideRow" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGrid}" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.RowStyle>
    

    and define the Converter to return Collapse if property's value is null, otherwise Visible.

    public class NullPropertyToBoolConverter2 : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values[0] is Obj obj && values[1] is string str)
            {
                return typeof(Obj).GetProperty(str)?.GetValue(obj, null) == null ? Visibility.Collapsed : Visibility.Visible;
            }
            return Visibility.Visible;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }