WPF: DataTrigger bound to a ContextMenu MenuItem w

2019-09-08 19:01发布

问题:

I am trying to hide colummns in a Datagrid (bound to a DataTable) with DataTrigger. So that the user can choose which columns are displayed. My problem is that this only works once. Normaly I would say that I need the INotifyPropertyChanged, however my ContextMenu is defined in XAML so I am not sure if and how to do that.

Here my XAML

<ContextMenu >
     ....
     <MenuItem Header="Apply" x:Name="ButtonApply" />
</ContextMenu>

<DataGrid ItemsSource="{Binding Path=TabDataTable}" AutoGenerateColumns="True" IsReadOnly="True" SelectionUnit="CellOrRowHeader" Margin="-5,-4,-5,-4" AlternatingRowBackground="Gainsboro" x:Name="MainDataGrid">
    <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridColumnHeader}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=ButtonApply, Path=IsPressed}" Value="True">
                    <Setter Property="Visibility">
                        <Setter.Value>
                            <!--<Binding Path=" DataContext.IsTemplateColumnVisibile, Source={StaticResource ProxyElement}, Converter={StaticResource BooleanToVisibilityConverter}}" />-->
                            <MultiBinding Converter="{StaticResource BooleanToVisibilityConverter}">
                                <Binding Path="Column" 
     RelativeSource="{RelativeSource Self}"/>
                                <Binding Path="DataContext.visibility" 
     RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Resources>
</DataGrid>

Here the Converter: (Sry for the misleading name)

public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (value[0] == null)
    {
        return null;
    }
    if ((((ObservableCollection<bool>)value[1]).ToArray())[((System.Windows.Controls.DataGridTextColumn)value[0]).DisplayIndex])
    {
        //Column Visibility
        ((System.Windows.Controls.DataGridTextColumn)value[0]).Visibility = Visibility.Visible;
        //Header Visibility
        return Visibility.Visible;
    }
    ((System.Windows.Controls.DataGridTextColumn)value[0]).Visibility = Visibility.Collapsed;
    return Visibility.Collapsed;
}  

If there is a better way to accomplish this, please feel free to point me in the right direction.

回答1:

You can use an Attached Property to control Column visibility and bind it to a list implementing INotifyPropertyChanged.

<Style TargetType="DataGrid">
        <Setter Property="local:DataGridHelper.VisibilityList"
                Value="{Binding NotifyingList}"/>
    </Style> 

Attached Property

public class DataGridHelper : DependencyObject
{
    public static List<bool> GetVisibilityList(
        DependencyObject obj)
    {
        return (List<bool>)obj.GetValue(VisibilityListProperty);
    }
    public static void SetVisibilityList(
        DependencyObject obj, List<bool> value)
    {
        obj.SetValue(VisibilityListProperty, value);
    }
    public static readonly DependencyProperty
        VisibilityListProperty =
        DependencyProperty.RegisterAttached("VisibilityList",
        typeof(List<bool>), typeof(DataGridHelper),
        new PropertyMetadata(VisibilityListChanged));

    private static void VisibilityListChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        var grid = d as DataGrid;
        if (grid == null 
         || grid.Columns.Count == 0 
         || grid.Columns[0].DisplayIndex == -1) return;

        var visibilities = (List<bool>)grid.GetValue(VisibilityListProperty);

        foreach (var column in grid.Columns)
        {
            if ((bool)visibilities[column.DisplayIndex])
                column.Visibility = Visibility.Visible;
            else
                column.Visibility = Visibility.Collapsed;
        }
    }
}

Note that column.DisplayIndex is used, so DataGrid.CanUserReorderColumns should be false. This should get you going, I suggest you try it out before you tackle the ContextMenu.