WPF中DataGridColumn的绑定可见性


75

如何DataGrid通过绑定在WPF中隐藏列?

这就是我所做的:

<DataGridTextColumn Header="Column header"
                    Binding="{Binding ColumnValue}"
                    Width="100"
                    ElementStyle="{StaticResource DataGridRightAlign}"
                    Visibility="{Binding MyColumnVisibility}" />

这就是我得到的(除了该列仍然可见):

System.Windows.Data错误:2:找不到目标元素的管理FrameworkElement或FrameworkContentElement。BindingExpression:Path = MyColumnVisibility; DataItem = null; 目标元素是'DataGridTextColumn'(HashCode = 1460142); 目标属性为“可见性”(类型为“可见性”)

如何解决绑定问题?

Answers:


175

首先DataGridTextColumn或其他任何受支持的dataGrid列都不位于的可视树中DataGrid。因此,在默认情况下它不会继承DataContextDataGrid。但是,它Binding仅适用于DP,而不适用于DataGridColumn上的其他DP。

因为它们并不位于同一VisualTree中,所以使用DataContext进行的任何尝试RelativeSource都将无法正常工作,因为DataGrid无法遍历DataGrid。

但是有两种方法可以实现:


首先使用Freezable类-Freezable即使对象不在视觉或逻辑树中,它们也可以继承DataContext。因此,我们可以利用它来进行使用。

首先创建从FreezableDataDP继承的类,我们可以使用它们在XAML中进行绑定:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
                                     typeof(BindingProxy));
}

现在,在DataGrid资源中添加它的一个实例,以便它可以继承DataGrid的DataContext,然后可以与其数据DP绑定:

    <DataGrid>
        <DataGrid.Resources>
            <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
                                                Source={StaticResource proxy}}"/>
        </DataGrid.Columns>
    </DataGrid>

其次,您可以使用ElementName或引用XAML中的任何UI元素x:Reference。但是ElementName只能在同一视觉树中使用,而x:Reference没有这种约束。

因此,我们也可以利用该优势。FrameworkElement在“可见性”设置为“折叠”的XAML中创建虚拟对象。FrameworkElement将从其父容器(可以是Window或UserControl)继承DataContext。

并可以在DataGrid中使用它:

    <FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Test"
                                Binding="{Binding Name}"
                                Visibility="{Binding DataContext.IsEnable,
                                          Source={x:Reference dummyElement}}"/>
        </DataGrid.Columns>
    </DataGrid>

3
我喜欢第二种方法。这很容易编写,而且我已经具有相同可见性的另一个控件,因此我可以给它x:Name加上一个对其Visibility属性的引用。并不是真正的直截了当,而是更多地转向侧面,但是仍然很简单。我猜想,当绑定到被引用元素的DataContext属性时,您会“劫持”另一个元素以与否则无法访问的DataGridColumn共享其DataContext,对吗?dummyElement只是桥梁。
ygoe 2014年

2
@LonelyPixel-是的,您说对了。我尝试从其DataGrid兄弟级子对象劫持DataContext,因为除非明确设置,否则它们都共享相同的DataContext。我本可以将x:Reference与DataGrid本身一起使用,但这将导致循环依赖性。
Rohit Vats 2014年

1
+1为您的答案。对不起,我误解了这个问题。通过x:Reference在WPF 4.0中使用-至少在Visual Studio 2010中可能仍会出现异常:Service provider is missing the INameResolver service,可以将其忽略。据我了解,它在WPF 4.5中已修复。
Anatoliy Nikolaev

2
就个人而言,如果我问我,我喜欢第一种方法。开销只是为了创建一个类,但是一旦您将它放进了书包,那么使用XAML进行编码就变得很容易。我确实经常使用它。
Rohit Vats 2014年

2
@JMIII不知道,我现在不在任何地方使用它。另外,只要最终运行XAML编辑器,我也不关心XAML编辑器了解什么(不是很多)。
ygoe

18
<Window.Resources>
    <ResourceDictionary>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
    </ResourceDictionary>
</Window.Resources>

<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
                  AutoGenerateColumns="False"
                  FlowDirection="LeftToRight"
                  ItemsSource="{Binding PayStructures}"
                  SelectedItem="{Binding SelectedItem}">
    <DataGrid.Columns>
         <DataGridTemplateColumn Width="70"
                                 Header="name"
                                 IsReadOnly="True"
                                 Visibility="{Binding DataContext.IsShowName,
                                 Source={StaticResource ProxyElement}}">
             <DataGridTemplateColumn.CellTemplate>
                 <DataTemplate>
                     <TextBlock Text="{Binding FieldName}" />
                 </DataTemplate>
             </DataGridTemplateColumn.CellTemplate>
         </DataGridTemplateColumn>                   
     </DataGrid.Columns>
</mch:MCHDataGrid>

视图模型中绑定属性的样本:

private Visibility _isShowName;

public Visibility IsShowName
{
    get { return _isShowName; }
    set
    {
        _isShowName = value;
        OnPropertyChanged();
    }
}

我想这已经在一年前提出了。太晚了。
ygoe '16

如果要打印当前DataContext的类,请使用以下命令:<TextBlock Text="{Binding DataContext, Source={StaticResource ProxyElement}}"></TextBlock>
Contango

如果数据上下文实际上不是静态的,则可能不起作用,但可能会有所不同。在这种情况下,我得到:“ System.Windows.Data错误:3:找不到提供DataContext的元素。BindingExpression :(无路径); DataItem = null;目标元素是'FrameworkElement'(名称='ProxyFrameworkElement');目标创建窗口时,属性为“ DataContext”(类型为“对象”)。
JS
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.