在项目中使用DataGrid需要根据业务动态隐藏某些列,思路都是给DataGrid中列的Visibility属性绑定值来实现(项目使用MVVM),如下
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Visibility="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}},Path=DataContext.IsVisible,Converter={StaticResource BooleanToVisibilityConverter}}"></DataGridTextColumn>
如果不是在DataGridTextColumn上绑定,那么这段代码是可以工作的,问题出在DataGridTextColumn上,我们来看它的继承层次
它不是FrameworkElement的派生类,所以它不具有DataContext属性,也就是说,即使DataGrid有DataContext,DataGridTextColumn也不能得到它,Visibility找不到DataContext自然绑定不上,这就是为什么上面的绑定失效的原因,同样其他类型的列也有同样的问题。那么如何优雅地解决问题呢?国际友人给出了这样一个方法,既然DataGridTextColumn不能从父级那里得到DataContext,那么直接给她的Visibility指定一个DataContext,实现如下
- 首先创建一个可以承载DataContext的绑定代理类BindingProxy
- 然后将这个BindingProxy当作资源,获得DataContext
- 将DataGridTextColumn的Visibility的DataContext指定为这个BindingProxy
BindingProxy.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; namespace WPF_DataContext_Sample { public class BindingProxy:Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object DataContext { get { return (object)GetValue(DataContextProperty); } set { SetValue(DataContextProperty, value); } } // Using a DependencyProperty as the backing store for DataContext. This enables animation, styling, binding, etc... public static readonly DependencyProperty DataContextProperty = DependencyProperty.Register("DataContext", typeof(object), typeof(BindingProxy), new PropertyMetadata(null)); } }
MainWindow.xaml
<Window x:Class="WPF_DataContext_Sample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfDataContextSample="clr-namespace:WPF_DataContext_Sample" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <wpfDataContextSample:Converter x:Key="Converter"></wpfDataContextSample:Converter> <wpfDataContextSample:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"></wpfDataContextSample:BindingProxy> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="0.1*"/> </Grid.RowDefinitions> <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Students}"> <DataGrid.Columns> <DataGridTextColumn Header="Id" Binding="{Binding Id}"></DataGridTextColumn> <!--这样的绑定会失败--> <!--<DataGridTextColumn Header="Name" Binding="{Binding Name}" Visibility="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}},Path=DataContext.IsShow,Converter={StaticResource BooleanToVisibilityConverter}}"></DataGridTextColumn>--> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Visibility="{Binding DataContext.IsShow,Source={StaticResource BindingProxy},Converter={StaticResource Converter}}"></DataGridTextColumn> <DataGridTextColumn Header="Sex" Binding="{Binding Sex}"></DataGridTextColumn> </DataGrid.Columns> </DataGrid> <StackPanel Orientation="Horizontal" Grid.Row="1"> <Button Width="100" Height="30" Content="隐藏Name" Click="ButtonBase_OnClick"></Button> </StackPanel> </Grid> </Window>
这样就能优雅地解决了这个问题