WPF DataGridTextColumn下的Visibility绑定
WPF中的DataContext属性非常方便,但在某些情况下,DataContext是不可访问的,比如,当你想绑定的元素不属于其逻辑树或可视树时,想正常使用绑定就可能非常困难……
让我们给一个简单的例子予以说明:我们要在DataGrid中显示产品列表。 在其中,我们希望能够基于ViewModel中公开的ShowPrice属性的值来显示或隐藏Price列。 按照我们正常的做法的话是将列的Visibility绑定到IsHide属性上,有需要的话再通过转化器来将值转换成Visibility枚举:
前台XAML代码如下
<Window
x:Class="WPFTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<local:VisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
<Grid>
<DataGrid
x:Name="DataGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding DataList}">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding Name}"
Header="名称"
IsReadOnly="True" />
<DataGridTextColumn
Binding="{Binding Content}"
Header="备注"
IsReadOnly="True" />
<DataGridTextColumn
Binding="{Binding Value}"
Header="值"
IsReadOnly="False"
Visibility="{Binding IsHide, Converter={StaticResource visibilityConverter}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
后台ViewModel代码如下
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.ObjectModel;
namespace WPFTest
{
public class MainViewModel : ObservableRecipient
{
private bool isHide = true;
public bool IsHide
{
get => isHide;
set => SetProperty(ref isHide, value);
}
private ObservableCollection<ItemModel> dataList = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> DataList
{
get => dataList;
set => SetProperty(ref dataList, value);
}
public MainViewModel()
{
InitData();
}
private void InitData()
{
for (int i = 0; i < 5; i++)
{
ItemModel model = new ItemModel();
model.Name = $"名称{i + 1}";
model.Content = $"备注{i + 1}";
Random random = new Random();
model.Value = random.Next(0, 100).ToString();
DataList.Add(model);
}
}
}
}
对应的Model代码如下
namespace WPFTest
{
public class ItemModel
{
public string Name { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
public bool IsHide { get; set; } = false;
}
}
转换器代码如下
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WPFTest
{
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
以上代码的运行效果如下
但是,我们很快就会发现这样并不起作用,"值"列会一直可见。同时我们可以看到Visual Studio下的XAML绑定失败中可以看到提示
IsHide DataGridTextColumn.Visibility Visibility 找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement
哪怕我们更改绑定成其他的绑定方式
<DataGridTextColumn
Binding="{Binding Value}"
Header="值"
IsReadOnly="False"
Visibility="{Binding IsHide, Converter={StaticResource visibilityConverter},RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
也是一样不生效的
此处可以采用Freezable类来处理这个问题,首先创建一个BindingProxy类的继承了Freezable的类,以及定义了一个名为Data的依赖项属性:
Freezable 提供 Changed 事件来通知观察者对象发生的任何修改。 冻结的 Freezable 还可以跨线程共享,解冻的 Freezable 则不能。
using System.Windows;
namespace WPFTest
{
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion Overrides of Freezable
public object Data
{
get
{
return (object)GetValue(DataProperty);
}
set
{
SetValue(DataProperty, value);
}
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
}
}
然后,我们可以在DataGrid的资源中声明此类的实例,并将Data属性绑定到当前的DataContext:
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
</DataGrid.Resources>
最后一步是指定此BindingProxy对象作为绑定的源:
<DataGridTextColumn
Binding="{Binding Value}"
Header="值"
IsReadOnly="False"
Visibility="{Binding Data, Converter={StaticResource visibilityConverter}, Source={StaticResource proxy}}" />
此处Data即是上面绑定的IsHide属性值(如果绑定的是诸如Class的话则对应的属性)
完整的XAML代码如下
<Window
x:Class="WPFTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<local:VisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
<Grid>
<DataGrid
x:Name="DataGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding DataList}">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding Name}"
Header="名称"
IsReadOnly="True" />
<DataGridTextColumn
Binding="{Binding Content}"
Header="备注"
IsReadOnly="True" />
<DataGridTextColumn
Binding="{Binding Value}"
Header="值"
IsReadOnly="False"
Visibility="{Binding Data, Converter={StaticResource visibilityConverter}, Source={StaticResource proxy}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
运行结果如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」