WPF DataGridColumn Visibility

本文关键字:Visibility DataGridColumn WPF | 更新日期: 2023-09-27 18:19:23

当我们需要根据某些条件隐藏DataGrid的列时,我们都遇到了问题。至少有两种方法可以解决这个问题。这两种方法都需要代理元素。这两种方法我都用。如您所见,使用FreezableProxy作为代理元素不需要ContentControl,但需要指定额外的类(FreezableProxy)。使用FrameworkElement作为代理不需要指定额外的类(如FreezableProxy),但需要将ContentControl添加到标记中。

XAML:

<Window.Resources>
    <SampleRadioBoxCheckedConverter:FreezableProxy x:Key="FreezableProxy" Data="{Binding}"/>
    <FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
</Window.Resources>
<Grid Margin="10">
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <DataGrid AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name"/>
            <DataGridTextColumn Header="Type" 
                                Visibility="{Binding Data.IsPartnerColumnVisible, Source={StaticResource FreezableProxy}}"/>
            <DataGridTextColumn Header="Number" 
                                Visibility="{Binding DataContext.IsPartnerColumnVisible, Source={StaticResource FrameworkElement}}"/>
        </DataGrid.Columns>
    </DataGrid>
    <ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>
</Grid>

后台代码:

public class FreezableProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new FreezableProxy();
    }
    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
                                        typeof(FreezableProxy));
}
public partial class MainWindow : INotifyPropertyChanged
{
    private Visibility _isPartnerColumnVisible = Visibility.Hidden;
    public Visibility IsPartnerColumnVisible
    {
        get
        {
            return _isPartnerColumnVisible;
        }
        set
        {
            _isPartnerColumnVisible = value;
            RaisePropertyChanged("IsPartnerColumnVisible");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    void RaisePropertyChanged(String prop)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
}

两种方法看起来非常相似。建议我们使用第二种方法(使用FrameworkElement)。然后我有一个问题-为什么我需要指定额外的ContentControl,如果Binding实际上设置为{StaticResource FrameworkElement} ?ContentControl有什么神奇之处?当我们使用FrezableProxy时,我们不需要指定任何ContentControl-s或其他东西,它工作得很好。

<FrameworkElement x:Key="FrameworkElement" DataContext="{Binding}"/>
/////<ContentControl Grid.Row="1" Content="{StaticResource FrameworkElement}" Visibility="Collapsed"></ContentControl>

为什么这不起作用?

WPF DataGridColumn Visibility

内容控制是注释,但我设置FrameworkElementDataContext属性。

问题是DataGridColumns不位于相同的可视化树作为它的父dataGrid。因此,绑定不能工作,因为DataContext不是继承的,不能使用RelativeSource找到,因为它依赖于Visual Tree。

所以,所有提到的方法都是将DataContext传递给列。


现在,两种方法的区别:

FreezableProxy方法:

实际的源代码参考是这里。Freezable背后的魔力是在这里定义的,所以引用相同的链接:

Freezable类的主要目的是定义具有可修改和只读状态,但是我们的情况是可冻结的对象可以继承DataContext,即使它们不在可视或逻辑树

所以,你不需要任何ContentControl或任何代理控件因为有了freezable,我们会自动继承DataContext。


FrameworkElement + ContentControl方法:

DataContext属性用标志 FrameworkMetadataOptions.Inherits 声明。除非为子控件显式地设置,否则子控件将自动从父元素继承它。

所以ContentControl自动从Grid继承DataContext,而ContentControl的子元素将从ContentControl继承它。这就是为什么FrameworkElement从ContentControl继承它。(无需手动绑定DataContext)

<FrameworkElement x:Key="FrameworkElement"/>

另一种方法是使用 x:Reference ,如我在回答中所描述的

你错了。所使用的元素不一定是ContentControl,可以是任何FrameworkElement。您似乎也对这种方法的实际工作方式感到困惑,因为您而不是Binding中使用所需的x:Reference指令:

<Grid Margin="10">
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <DataGrid AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name"/>
            <DataGridTextColumn Header="Number" 
                Visibility="{Binding DataContext.IsPartnerColumnVisible, 
                Source={x:Reference FrameworkElement}}"/>    
        </DataGrid.Columns>
    </DataGrid>
    <FrameworkElement Name="someElement" Grid.Row="1" Visibility="Collapsed" />    
</Grid>

这个问题是因为DataGridTextColumn而不是主可视树的部分。我们可以在这里使用x:Reference指令,因为它不依赖于源元素与使用它的Binding在同一可视树中。简而言之,我们只是在这里使用这个FrameworkElement(它可以是任何控件),这样我们就可以通过它从主视觉树访问DataContext

相关文章: