WPF 中 DataGridColumn 的绑定可见性

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

我就是这么做的:

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

这就是我得到的(除了仍然可见的柱子之外) :

系统。窗户。数据错误: 2: 找不到控制目标元素的 FrameworkElement 或 FrameworkContentElement。BindingExpression: Path = MyColumnVisiability; Dataltem = null; target 元素是“ DataGridTextColumn”(HashCode = 1460142) ; target 属性是“ Visiability”(类型为“ Visiability”)

如何修复绑定?

58336 次浏览

First of all, ABC0 (or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid. Hence, by default it doesn't inherit the ABC2 of the DataGrid. However, it works for Binding DP only and for no other DP's on DataGridColumn.

Since they don't lie in the same VisualTree, any attempt to get the DataContext using RelativeSource won't work as well because DataGridTextColumn is unable to traverse up to the DataGrid.

There are two other ways to achieve this though:


First using a Freezable class. Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.

First, create a class inheriting from Freezable and Data DP which we can use to bind in 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));
}

Now, add an instance of it in DataGrid resources so that it can inherit the DataGrid's DataContext and can bind with its Data 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>

Second, you can refer to any UI element in XAML using ElementName or x:Reference. However, ElementName works only in the same visual tree, whereas x:Reference doesn't have such constraints.

So, we can use that as well to our advantage. Create a dummy FrameworkElement in XAML with Visibility set to collapsed. The FrameworkElement will inherit the DataContext from its parent container, which can be a Window or UserControl.

And can use that in 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>
<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>

Sample of bound property in view model:

private Visibility _isShowName;


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

Another easy solution I like is to add a dummy collapsed FrameworkElement at the same level as the DataGrid. The FrameworkElement can then be used as the Source of the Binding with the x:Reference markup extension.

For example like this:

<FrameworkElement x:Name="FrameWorkElementProxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.DataGridColumnVisibility, Source={x:Reference Name=FrameWorkElementProxy}}"/>
</DataGrid.Columns>
</DataGrid>