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属性正确显示或隐藏该列

 

posted @   信息技术的风采  阅读(8)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示