【问题标题】:Where is WPF setting GridViewRowPresenter.Columns for GridViewWPF 在哪里为 GridView 设置 GridViewRowPresenter.Columns
【发布时间】:2014-02-12 07:24:25
【问题描述】:

我最近正在探索一个sample,在TreeView 中使用GridViewRowPresenter 使其像一个“treegrid”。为此,通常需要定义一个GridViewColumnCollection,并将其显式设置为GridViewRowPresenter.Columns。该示例非常适合我。

但是,由于GridViewRowPresenter 最常与ListView 和GridView 一起使用。我开始想知道ListView 和GridView 是如何为每一行设置Columns 属性的。 xaml 最有可能是这样的:

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Length" DisplayMemberBinding="{Binding}"/>
        </GridView>
    </ListView.View>
    <ListViewItem>Hello</ListViewItem>
    <ListViewItem>World</ListViewItem>
</ListView>

请注意,虽然GridViewColumnCollection 是明确定义的,但它并未在任何地方为ListViewItem 设置。最终每个ListViewItem 都将包含一个GridViewRowPresenter,但在没有设置Columns 属性的位置。我检查了ListView.cs、ListView.xaml、ListViewItem.cs、ListViewItem.xaml、GridViewRowPresenter.csGridViewRowPresenterBase.cs 的开源代码,其中没有设置 Columns 属性。

我找到的最接近的东西是GridView.ColumnCollection 属性,我怀疑它在连接GridViewRowPresenter.Columns 和GridView.Columns 方面起到了一些作用,但是如何?

【问题讨论】:

    标签: c# wpf xaml gridview


    【解决方案1】:

    GridView.ColumnCollection 似乎是您正在寻找的东西。

    GridViewRowPresenter 拥有相同的 ColumnCollection 实例,一旦排列其单元格,它就会遍历列列表以获取其他信息,例如所需的宽度或所需的 DisplayMemberBinding。

    还有一个 GridViewHeaderPresenter,它的作用与 RowPresenter 相同,只是不是每行创建单元格,而是创建标题。

    编辑:

    您可以在反射代码后的语句后看到。

    protected internal override void PrepareItem(ListViewItem item)
    {
        base.PrepareItem(item);
    
        // attach GridViewColumnCollection to ListViewItem.
        SetColumnCollection(item, _columns);
    }
    

    这就是 GridViewColumnCollection 被传递/附加到 ListViewItem 的方式。

    你可以看到它的内部。

    【讨论】:

    • 我查看了 ListView 的默认样式,有这样的东西
    • "我查看了 ListView 的默认样式,有这样的 集合。
    • 那么我的问题是,GridViewRowPresenter 对应的设置语句是什么?我们只找到了“标题”的设置,而不是“行”的设置,对吧?
    • 什么?你试过谷歌搜索吗? GridViewRowPresenter 也有 Columns 属性。例如: 看这里:msdn.microsoft.com/en-us/library/ms752313(v=vs.110).aspx 对应的设置语句是什么意思?
    • 如果您自己使用 GridViewRowPresenter,是的,您需要手动设置 Columns 属性。我要问的是 WPF 在默认情况下如何为 ListView-GirdView 执行此操作。如果我在原始问题中使用代码。您将无法在任何地方找到 columns 属性的设置器。不在源代码或任何正在应用的样式中。我不是在问应该如何使用控件,我是想弄清楚 WPF 本身默认情况下是如何做到的
    【解决方案2】:

    我将展示如何在运行时探究 GridViewColumn 和 GridViewRowPresenter 交互。考虑以下简化的 XAML:

    <ListView x:Class="demo_listview"
        xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wd="clr-namespace:System.Windows.Documents;assembly=PresentationFramework"
        VirtualizingStackPanel.IsVirtualizing="True" 
        VirtualizingStackPanel.VirtualizationMode="Recycling">
    
        <!-- ... -->
    
        <ListView.View>
            <GridView>
    
                <!-- ... -->
    
                <GridViewColumn x:Name="my_gv_column" Width="100">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock x:Name="my_textblock" Text="ABCD" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
    
                <!-- ... -->
    
            </GridView>
        </ListView.View>
    </ListView>
    

    现在在代码中,重写诸如ListView.ClearContainerForItemOverride 之类的方法,如下所示。代码会修改TextBlock的背景色,使其变为红色:

    请注意,由于容器被回收,红色在“清理”后仍然存在。我选择该函数是为了保持演示简单,因为它确保模板将被完全实例化以供检查。否则,例如,如果您得到 gv_row 的“null”,则需要立即调用 lvi.ApplyTemplate()。

    我假设您能够轻松地为您感兴趣的行获取ListViewItem。因此,从运行时任何完全实例化的ListViewItem,我们现在可以看到如何获取特定的内容在运行时实例化CellTemplate。附加说明在代码下方。

    public partial class demo_listview : ListView
    {
        protected override void ClearContainerForItemOverride(DependencyObject el, object item)
        {
            base.ClearContainerForItemOverride(el, item);
    
            var lvi = (ListViewItem)el;
    
            var gv_row = (GridViewRowPresenter)lvi.Template.FindName("5_T", lvi);
    
            var ix = ((GridView)View).Columns.IndexOf(my_gv_column);
            var cp = (ContentPresenter)VisualTreeHelper.GetChild(gv_row, ix);
    
            var tb = (TextBlock)my_gv_column.CellTemplate.FindName("my_textblock", cp);
            tb.Background = Brushes.Red;
        }
    };
    

    第一步是从ListViewItem 获取GridViewRowPresenter。有几种方法可以做到这一点。上面,我使用魔术常量“5_T”从模板名称中获取实例化。此常量由 WPF 定义,因此只要您使用标准的ListViewItem 其内置控件模板,它就可以工作。

    接下来我们需要我们感兴趣的列的索引。在 C# 中通过在 XAMLmy_gv_column) 可以轻松访问列实例本身/强>。至于它的索引,我不确定如果列重新排序,这里显示的IndexOf 技术是否会继续工作。如果不是,则可以通过反射从感兴趣的GridViewColumn 获取私有_actualIndex 字段(或ActualIndex 属性)的值来获得正确的结果。

    接下来,使用VisualTreeHelper.GetChild,获取与所需列索引相对应的GridViewRowPresenter 的可视子项;这将是单元格的ContentPresenter!有了这个,您可以在模板中进行另一个FindName 搜索,以按名称查找您正在寻找的实际实例化控件。在示例中,我们成功获取了TextBlock 实例,然后更改其背景颜色以演示结果。

    【讨论】:

      【解决方案3】:

      在 FrameworkElementFactory.ApplyAutoAliasRules 中完成:

                  // GridViewRowPresenter auto-aliases Content and Columns to Content
                  // property GridView.ColumnCollection property on the templated parent.
      

      https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/FrameworkElementFactory.cs,9ce27c5244be816c,references

      所以,GridViewRowPresenter.Columns 默认为 {TemplateBinding GridView.ColumnCollection}。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-02
        • 2020-03-07
        • 2016-12-25
        相关资源
        最近更新 更多