千家信息网

自动扩张WPF树型表格列宽问题怎么解决

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,今天小编给大家分享一下自动扩张WPF树型表格列宽问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面
千家信息网最后更新 2025年11月07日自动扩张WPF树型表格列宽问题怎么解决

今天小编给大家分享一下自动扩张WPF树型表格列宽问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

原问题如下:

图1 问题描述

背景

树型控件在GIX4系统中已经被大量使用。这个控件是一年前其它同事在网上搜索到,再引入的。

一开始的时候,要解决这个问题,想到的最直接的方案是这样的:找到***列中的Expander控件(加号:),然后监听它的"Expanded"事件;在事件处理程序中,计算所需要的宽度,然后设置为控件的宽度。

按照这个方案去实际写代码时,发现并没有想象中那么简单,发现了很多问题。例如,Expander并不是Expander控件,而是一个ToggleButton,而且是写在模板中的,TreeGridRowPresenter中的Expander的类型也只是UIElement,也就是说,不能把Expander从UIElement转换为ToggleButton,这样程序会写得很死。又如,如何计算***列的所需要宽度。

这些问题是要上面提及的BUG所需要解决的:

四个待解决的问题

1. 何时触发是最合适的?在何处触发调整宽度的代码?

2. 如何找到树型控件的所有GridViewRowPresenter。

3. GridViewRowPresenter中,如何把***列的控件找到。

4. ***列控件的组成结构是怎么样的,它所需要的大小如何求出,是否可以直接使用Measure和DesiredSize。

一步一步解决

***个问题,何时触发这个功能?其实我是要在点击后,当子节点都加载好后,然后计算出合适的大小,再设置给列对象。我先在TreeListView的OnExpanded事件处理程序中尝试编写代码获取每一个TreeListView,但是发现这个事件在发生时,所有的子节点并没有生成,所以不能通过ItemContainerGenerator.GetContainerForItem方法获取到窗口,此方案失败。接着,我查看了ItemsControl的接口声明,发现ItemContainerGenerator属性有事件StatusChanged。所以我就改为监听这个事件,并判断如果当它的Status变为ContainersGenerated时,就表示所有子节点已经生成了。代码如下:

this.ItemContainerGenerator.StatusChanged += (o, e) =>     {    if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)      {          this.AdjustFirstColumnWidth();       }     };

但是同样发现新的问题,这时候虽然窗口对象TreeListView已经生成,但是它下面的所有Visual Child都没有生成,这样同样无法获取到它里面用来显示每一行的GridRowPresenter。所以只有改成了这样:

 public TreeListViewItem()     {    this.PrepareToAdjustFirstColumnWidth();     }     private void PrepareToAdjustFirstColumnWidth()     {      this.ItemContainerGenerator.StatusChanged += (o, e) =>       {         if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)        {           if (this.Items.Count > 0)           {         var item = this.Items[this.Items.Count - 1];    var treeItem = this.ItemContainerGenerator.ContainerFromItem(item) as TreeListViewItem;    treeItem.Loaded += (oo, ee) =>       {          this.AdjustFirstColumnWidth();       };        }        }        };     }

这样,***一个孩子的可视内容都加载好后,才会触发调整宽度的代码。

第二个问题比较简单,看了TreeListView的源码后,发现它在TreeListViewItem类的模板中使用了GridViewRowPresenter类,然后为它定义了名字:"PART_Header"。

private TreeGridViewRowPresenter FindGridRow()    {    var rowPresenter = this.Template.FindName("PART_Header", this) as TreeGridViewRowPresenter;     return rowPresenter;    }

要解决第三个问题,我们需要知道GridViewRowPresenter中如何生成一行,并知道***生成的控件结构。先看看GridViewRowPresenter***生成的控件结构,这里我使用的是Snoop:

图2 用Snoop查看TreeGridViewRowPresenter的可视化结构

我们发现,GridViewRowPresenter下只是简单的包含了几个可视元素,它们刚好是每一列所显示的内容。再查看GridViewRowPresenter的源代码,发现它拥有以下属性:public GridViewColumnCollection Columns{get;set;}、internal UIElementCollection InternalCollection{get;set;},进一步分析后,我猜测性地得出以下结论:GridViewRowPresenter.InternalCollection简单地包含了所有列的显示元素,它会根据Columns属性中各行对这些可视元素进行维护,让它们显示得跟表格一样。

至此,第三个问题解决了:

var firstColumn = VisualTreeHelper.GetChild(rowPresenter, 0) as UIElement;

***一个问题,是过程中最麻烦的一个问题。我们看到,图2中该行下的***个元素是***列的显示元素,显示了"2.1"。但是文本左边的Expander控件却是TreeGridViewRowPresenter的***一个可视化孩子。而且缩进并不是一个控件。那么这是怎么一回事呢?看了TreeGridViewRowPresenter的源码后,发现原来是它主动把Expander放在了***:

public class TreeGridViewRowPresenter : GridViewRowPresenter    {      protected override System.Windows.Media.Visual GetVisualChild(int index)       {         // Last element is always the expander       // called by render engine      if (index < base.VisualChildrenCount) return base.GetVisualChild(index);     if (index == base.VisualChildrenCount) return this.lbRowNo;     return this.Expander;        }      protected override int VisualChildrenCount     {      get        {            // Last element is always the expander          if (this.Expander != null)            return base.VisualChildrenCount + 2;       else         return base.VisualChildrenCount + 1;       }       }    }

而文本前面先显示缩进,然后再显示Expander的原因是由于TreeGridViewRowPresenter类重写了FrameworkElement.ArrangeOverride方法。在该方法中,它把***列的元素显示的长度变短在之前显示一段缩进的空白和Expander控件:

protected override Size ArrangeOverride(Size arrangeSize)     {     Size s = base.ArrangeOverride(arrangeSize);    if (this.Columns == null || this.Columns.Count == 0) return s;    UIElement expander = this.Expander;    double current = 0;    double max = arrangeSize.Width;     for (int x = 0; x < this.Columns.Count; x++)    {      GridViewColumn column = this.Columns[x];      // Actual index needed for column reorder      UIElement uiColumn = (UIElement)base.GetVisualChild((int)ActualIndexProperty.GetValue(column, null));     // Compute column width      double w = Math.Min(max, (Double.IsNaN(column.Width)) ? (double)DesiredWidthProperty.GetValue(column, null) : column.Width);     // First column indent      if (x == 0 && expander != null)      {        double indent = FirstColumnIndent + expander.DesiredSize.Width;       uiColumn.Arrange(new Rect(current + indent, 0, w - indent, arrangeSize.Height));          }         else       {          uiColumn.Arrange(new Rect(current, 0, w, arrangeSize.Height));         }      max -= w;         current += w;        }      // Show expander     if (expander != null)        {      expander.Arrange(new Rect(this.FirstColumnIndent, 0, expander.DesiredSize.Width, expander.DesiredSize.Height));      }      return s;    }

分析到这里,就知道如何计算出***列的最终宽度了:

private double GetFirstColumnDesiredWidth()     {      var rowPresenter = this.FindGridRow();     if (VisualTreeHelper.GetChildrenCount(rowPresenter) <= 0) return 0;     //GridViewRowPresenter中的每一个元素表示一列。     var firstColumn = VisualTreeHelper.GetChild(rowPresenter, 0) as UIElement;    var desiredWidth = firstColumn.DesiredSize.Width;     //需要的宽度前,需要加上列的缩进和Expander的宽度。     var indent = rowPresenter.FirstColumnIndent + rowPresenter.Expander.DesiredSize.Width;      return indent + desiredWidth + ENSURE_SIZE;     }

以上就是"自动扩张WPF树型表格列宽问题怎么解决"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

问题 控件 宽度 元素 生成 事件 代码 内容 知识 篇文章 结构 表格 属性 方案 方法 程序 节点 合适 一行 三个 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 宁波海曙捷迅软件开发服务部 网络安全攻防大赛的意义何在 软件开发dod 中专计算机网络技术难学吗 网络安全培训机构哪家好知乎 网页链接显示数据库出错 肇庆嵌入式软件开发可信吗 河南商客通软件开发有限公司 网络安全保障专班方案 豪猪兽 数码兽数据库 sql2016服务器名称是什么 河北网络技术咨询五星服务 数据库技术及应用刘国燊答案 服务器设备名称 数据库实时打印日志文件 益阳dell服务器总代理 网络技术主管是什么意思 陕西安卓软件开发服务商 软件开发行业劳动力成本 浪潮 4路 服务器 网络安全教育周手抄报图片 剑网三缘起服务器推荐 斯坦福大学的数据库课程 公检法行业软件开发商的发展 什么书说网络技术 网络安全网站整改情况汇报 交换机管理软件开发 门到门互联网科技有限公司 u8服务器电脑要求 消防大队网络安全等级评估报告
0