使用FrameworkElement存储用户控件数据上下文

问题描述 投票:0回答:1

我已经设法解决了我在尝试从 DataGrid 控件内部访问视图模型的属性时遇到的问题。另一个问题是我不太明白为什么会这样。

所以基本上解决方案是将 UserControl 的 DataContext 存储在 FrameWorkElement 中,如下所示

<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 中引用此 FrameWorkElement,(这是根据我的目标进行调整的)

<DataGridTextColumn Header="{StaticResource MPN}" Binding="{Binding ManufacturerPartNumber}" Width="*"
                Visibility="{Binding DataContext.ShowMpn, Source={StaticResource ProxyElement}, Converter={StaticResource BoolToVisisbilityCollapsed}}"/>

但这还不够,您还必须定义一个 ContentControl,将 FrameWorkElement 保存为内容,如下所示

<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>

所以我最终得到了这个观点

<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>

<StackPanel>
    <ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
    <DataGrid ItemsSource="{Binding ReelsRFL}" AutoGenerateColumns="False" ColumnWidth="*" Style="{StaticResource CustomDataGridStyle}">
        <DataGrid.CellStyle>
            <Style TargetType="DataGridCell" BasedOn="{StaticResource CustomDataGridCellStyle}">
                <Setter Property="Foreground">
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource BoolToHighlight}">
                            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="DataContext.ShowHighlight"/>
                            <Binding Path="RflHours"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.CellStyle>
        <DataGrid.Columns>
            <DataGridTextColumn Header="{StaticResource REELCODE}" Binding="{Binding ReelCode}" Width="*"
                                Visibility="{Binding DataContext.ShowReelCode, Source={StaticResource ProxyElement}, Converter={StaticResource BoolToVisisbilityCollapsed}}"/>

            <DataGridTextColumn Header="{StaticResource ITEMCODE}" Binding="{Binding ItemCode}" Width="*"
                                Visibility="{Binding DataContext.ShowPartNumber, Source={StaticResource ProxyElement}, Converter={StaticResource BoolToVisisbilityCollapsed}}"/>

            <DataGridTextColumn Header="{StaticResource MPN}" Binding="{Binding ManufacturerPartNumber}" Width="*"
                                Visibility="{Binding DataContext.ShowMpn, Source={StaticResource ProxyElement}, Converter={StaticResource BoolToVisisbilityCollapsed}}"/>

            <DataGridTextColumn Header="{StaticResource HRS}" Binding="{Binding RflHours}" Width="Auto">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell" BasedOn="{StaticResource CustomDataGridCellStyle}">
                        <Setter Property="Foreground">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource BoolToHighlight}">
                                    <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}" Path="DataContext.ShowHighlight"/>
                                    <Binding Path="RflHours"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="HorizontalAlignment" Value="Center"/>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</StackPanel>

我不明白为什么我需要 ContentControl 才能使一切正常工作。在元素内部存储数据上下文并在代码中使用它的想法很清楚。但我没有得到 ContentControl 的角色,因为它没有被引用。

如果有人能解释那就太好了。非常感谢:)

c# wpf
1个回答
0
投票

DataContext
DataGrid
不被列对象继承是正确的,因为它们不在可视化树中。
DataGridColumn
只是一个布局占位符,不会被渲染。
DataGridColumn
甚至不是
Visual
类型。相反,
DataGrid
会渲染
DataGridRow
DataGridCell
项目容器。这些容器的
DataContext
是当前行项目的指定属性值(如果源是
DataTable
,则为单元格值)。

这意味着您必须将单元格绑定到

ReelsRFL
源集合中的项目。我的意思是,当您从不同的数据源获取数据时,将
DataGrid
绑定到此集合有什么意义?这些数据如何映射到行?看起来您正在绑定到同一源对象(您的“ProxyElement”)的属性。所有行将显示相同的数据。

但是除了自动生成的列之外,也许这些是附加的静态列?如果是这种情况,那么您应该更改设计以摆脱丑陋的“ProxyElement”。 我建议将视图模型中的静态列数据和动态列数据合并到单个数据源中,例如

DataTable

关于

ContentControl
:除非您将“ProxyElement”添加到可视化树中,否则无法解析针对
DataContext
的绑定。如果您将“ProxyElement”添加到可视化树中,例如通过将其托管在
ContentControl
中,绑定变得多余,因为“ProxyElement”自然会继承
DataContext
ContentControl
。这意味着您可以直接绑定到
ContentControl
并删除“ProxyElement”。

另一个关键的 XAML 引擎行为是“ProxyElement”是一个

FrameworkElement
,它本身也是一个
UIElement

UIElement
定义为资源会导致实例化该单个对象。对此对象的任何引用都将产生相同的实例。该实例是共享的。这与样式或模板不同,其中每个引用都会创建样式或模板的新实例。 现在,使用
ContentControl
将“ProxyElement”添加到可视化树将加载它(调用
FrameworkElement.Loaded
)并解析数据绑定并启用 DataContext 继承,因为资源中的“ProxyElement”与由
ContentControl
(共享实例)托管,您可以将“ProxyElement”引用为资源,而无需通过
ContentControl
引用它。

我希望您看到

ContentControl
的冗余,因为您可以直接添加“ProxyElement”来代替
ContenControl

关于您的控件设计:一般而言,为

UserControl
或自定义控件提供
DataContext
的认识始终是一个糟糕的设计。您
UserControl
不应使用
DataContext
,因为这是为客户端上下文中的外部绑定声明保留的。 DataContext 必须根据控件的使用情况而发生变化。

相反,定义依赖属性来访问外部数据。然后客户端可以提供该数据,例如通过数据绑定。

例如,实现类型为

ItemsSource
IList
依赖属性并按如下方式使用它:

客户端代码

<!-- 
     ReelsRFL should be merged source (e.g. DataTable) 
     that contains data for both the static and dynamic columns 
-->
<ReelsRFLInfoView ItemsSource="{Binding ReelsRFL}" />

控制内部结构

<UserControl x:Name="Root">
  <DataGrid ItemsSource="{Binding ElementName=Root, Path=ItemsControl}" />
</UserControl>
© www.soinside.com 2019 - 2024. All rights reserved.