WPF中为何需ContentControl配合FrameworkElement存储DataContext?
我已经解决了在WPF的DataGrid控件内部访问ViewModel属性的问题,但不清楚方案生效的核心原因。
我的解决方案核心是在UserControl的资源中定义一个FrameworkElement来存储UserControl的DataContext:
<UserControl x:Class="EA.Dashboard.Console.WPF.Views.ReelsRFLInfoView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:EA.Dashboard.Console.WPF.Views" xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" HorizontalAlignment="Stretch" d:DesignHeight="450" d:DesignWidth="800" x:Name="this"> <UserControl.Resources> <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/> </UserControl.Resources>
之后在DataGrid的列中通过这个ProxyElement来绑定ViewModel的属性:
<DataGridTextColumn Header="{StaticResource MPN}" Binding="{Binding ManufacturerPartNumber}" Width="*" Visibility="{Binding DataContext.ShowMpn, Source={StaticResource ProxyElement}, Converter={StaticResource BoolToVisisbilityCollapsed}}"/>
但仅这样设置是无效的,必须添加一个隐藏的ContentControl来承载这个ProxyElement:
<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
我理解用元素存储DataContext供后续绑定的思路,但不明白为什么一定要加这个未直接使用的ContentControl才能让方案生效,希望有人解释它的作用。
核心原因解析
这个ContentControl的作用是把资源中的ProxyElement加入到WPF的逻辑树中,这是让绑定生效的关键,具体原因分三点:
资源字典中的元素默认不在逻辑树内
WPF资源字典里定义的对象,只有被添加到逻辑树或视觉树中时,才会继承父级元素的DataContext。你在UserControl.Resources里定义的ProxyElement,默认只是一个“待使用”的资源对象,它不在UserControl的逻辑树中,所以DataContext="{Binding}"没有绑定源——因为没有父级元素传递DataContext给它,此时它的DataContext是空的,自然没法供DataGrid列绑定使用。ContentControl让ProxyElement接入逻辑树
当你把ProxyElement设置为ContentControl的Content时,这个FrameworkElement就被纳入了UserControl的逻辑树体系中。此时它的DataContext="{Binding}"就能正确继承UserControl的DataContext(也就是你的ViewModel)。DataGrid列的特殊地位
DataGrid的列(比如DataGridTextColumn)本身并不在WPF的视觉树或逻辑树中,它们只是列的配置对象,没法直接通过RelativeSource找到UserControl来绑定ViewModel属性。所以必须借助一个已经在逻辑树中、持有ViewModel DataContext的代理元素(也就是ProxyElement),才能让列的属性(比如Visibility)绑定到ViewModel的属性上。
内容的提问来源于stack exchange,提问作者Alberto

