WPF在DataGrid的Column中绑定Visibility属性,显示绑定异常Cannot find governing FrameworkElement or FrameworkContentElement
参考原文: https://thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
翻译原文:https://blog.csdn.net/wdyxyyp/article/details/114486570
WPF中的DataContext属性非常方便,但在某些情况下,DataContext是不可访问的,比如,当你想绑定的元素不属于其逻辑树或可视树时,想正常使用绑定就可能非常困难……
让我们给一个简单的例子予以说明:我们要在DataGrid中显示产品列表。 在其中,我们希望能够基于ViewModel中公开的ShowPrice属性的值来显示或隐藏Price列。 一种明显的方法是将列的Visibility绑定到ShowPrice属性:
<DataGridTemplateColumn Header="| 类型" Width="104" Visibility="{Binding EnableSelectTagSense, Converter={StaticResource Boolean2VisibilityConverter }, Source={StaticResource proxy}}" >
但是,我们很快就会发现这样并不起作用,EnableSelectTagSense列会一直可见。为什么?如果我们看一下Visual Studio的Output窗口,会发现以下几行提示:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=EnableSelectTagSense; DataItem=null; target element is ‘DataGridTemplateColumn’ (HashCode=32685253); target property is ‘Visibility’ (type ‘Visibility’)
提示信息比较晦涩,但其实意思很简单:WPF不知道使用哪个FrameworkElement来获取DataContext,因为该列不属于DataGrid的逻辑树或可视树。
本问题使用下面两种方法都 没生效
方法一:
1 2 3 4 | <DataGridTextColumn Header= "Price" Binding= "{Binding Price}" IsReadOnly= "False" Visibility="{Binding DataContext.ShowPrice, Converter={StaticResource visibilityConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/> |
方法二:
1 2 3 4 | <DataGridTextColumn Header= "Price" Binding= "{Binding Price}" IsReadOnly= "False" Visibility="{Binding IsChecked, Converter={StaticResource visibilityConverter}, ElementName=chkShowPrice}"/> |
当然还有一种不使用MVVM模式的方式更新,直接在XAML.cs文件中进行联动进行调整。不过这种方式 个人不推荐,但可以解决问题。
经过查资料,发现使用MVVM模式需要支持动态支持已经生成列的内容的Visibility等属性,需要XAML继承自 Freezable 类,然后在该页面的XAML中绑定该命名空间。
Freezable原本目的是定义具有可修改状态和只读状态的object,但有趣的是,Freezable可以继承那些甚至不在逻辑树或可视树中的DataContext。我目前还不清楚其背后的运行机理,但是我们可以利用这个优势完成我们的绑定工作。
方法是创建一个我称之为BindingProxy的继承了Freezable的类,以及定义了一个名为Data的依赖项属性:该段代码可以直接COPY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } } // 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:
1 2 3 | <DataGrid.Resources> <local1:BindingProxy x:Key= "proxy" Data= "{Binding}" /> </DataGrid.Resources> |
最后一步是指定此BindingProxy对象作为绑定的源:
1 2 3 4 | <DataGridTextColumn Header= "Price" Binding= "{Binding Price}" IsReadOnly= "False" Visibility="{Binding Data.ShowPrice, Converter={StaticResource visibilityConverter}, Source={StaticResource proxy}}"/> |
需要注意,绑定路径的前缀应为“ Data”,因为该路径现在是相对于BindingProxy对象的。完成之后,绑定就可以生效,根据ShowPrice属性正确显示或隐藏该列
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现