成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

由一個(gè)Bug引出的自動(dòng)擴(kuò)張WPF樹型表格列寬問題

開發(fā) 后端
在這里我們將從作者所經(jīng)歷的一個(gè)Bug開始談起,主要涉及自動(dòng)擴(kuò)張WPF樹型表格列寬問題,希望對(duì)大家有所幫助。

問題描述

今天測(cè)試人員提了一個(gè)易用性的BUG,主要是說系統(tǒng)目前使用的樹型控件不支持自動(dòng)擴(kuò)張列的寬度。其實(shí)客戶那邊已經(jīng)對(duì)這個(gè)問題提了多次,不過由于對(duì)WPF只是入門級(jí),所以一直都沒改。這兩天項(xiàng)目比較閑,就花了些時(shí)間把這個(gè)問題改了。原問題如下:

image 

圖1 問題描述

背景

樹型控件在GIX4系統(tǒng)中已經(jīng)被大量使用。這個(gè)控件是一年前其它同事在網(wǎng)上搜索到,再引入的。

一開始的時(shí)候,要解決這個(gè)問題,想到的最直接的方案是這樣的:找到***列中的Expander控件(加號(hào):image),然后監(jiān)聽它的“Expanded”事件;在事件處理程序中,計(jì)算所需要的寬度,然后設(shè)置為控件的寬度。

按照這個(gè)方案去實(shí)際寫代碼時(shí),發(fā)現(xiàn)并沒有想象中那么簡(jiǎn)單,發(fā)現(xiàn)了很多問題。例如,Expander并不是Expander控件,而是一個(gè)ToggleButton,而且是寫在模板中的,TreeGridRowPresenter中的Expander的類型也只是UIElement,也就是說,不能把Expander從UIElement轉(zhuǎn)換為ToggleButton,這樣程序會(huì)寫得很死。又如,如何計(jì)算***列的所需要寬度。

雖然我們項(xiàng)目中是有整個(gè)控件的源碼,但是整合進(jìn)來后別的同事已經(jīng)對(duì)它進(jìn)行了很多修改,所以只有在網(wǎng)上找到最原始的源碼來研究。發(fā)現(xiàn),原來這個(gè)樹型控件的方案是Avalon Team自己給出的:《TreeListView: Show Hierarchy Data with Details in Columns》。然后Ricciolo對(duì)它進(jìn)行了一些研究:《Fun With GridView*RowPresenter》,***他給出了一個(gè)較完整的版本:《A complete WPF TreeListView control》。

學(xué)習(xí)并研究了它的源碼,***總結(jié)出以下幾個(gè)子問題,這些問題是要上面提及的BUG所需要解決的:

四個(gè)待解決的問題

1. 何時(shí)觸發(fā)是最合適的?在何處觸發(fā)調(diào)整寬度的代碼?

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

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

4. ***列控件的組成結(jié)構(gòu)是怎么樣的,它所需要的大小如何求出,是否可以直接使用Measure和DesiredSize。

一步一步解決

***個(gè)問題,何時(shí)觸發(fā)這個(gè)功能?其實(shí)我是要在點(diǎn)擊后,當(dāng)子節(jié)點(diǎn)都加載好后,然后計(jì)算出合適的大小,再設(shè)置給列對(duì)象。我先在TreeListView的OnExpanded事件處理程序中嘗試編寫代碼獲取每一個(gè)TreeListView,但是發(fā)現(xiàn)這個(gè)事件在發(fā)生時(shí),所有的子節(jié)點(diǎn)并沒有生成,所以不能通過ItemContainerGenerator.GetContainerForItem方法獲取到窗口,此方案失敗。接著,我查看了ItemsControl的接口聲明,發(fā)現(xiàn)ItemContainerGenerator屬性有事件StatusChanged。所以我就改為監(jiān)聽這個(gè)事件,并判斷如果當(dāng)它的Status變?yōu)镃ontainersGenerated時(shí),就表示所有子節(jié)點(diǎn)已經(jīng)生成了。代碼如下:

  1. this.ItemContainerGenerator.StatusChanged += (o, e) =>    
  2.  {    
  3. if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)    
  4.   {    
  5.       this.AdjustFirstColumnWidth();    
  6.    }    
  7.  };   

但是同樣發(fā)現(xiàn)新的問題,這時(shí)候雖然窗口對(duì)象TreeListView已經(jīng)生成,但是它下面的所有Visual Child都沒有生成,這樣同樣無法獲取到它里面用來顯示每一行的GridRowPresenter。所以只有改成了這樣:

  1.  public TreeListViewItem()    
  2.  {    
  3. this.PrepareToAdjustFirstColumnWidth();    
  4.  }    
  5.  private void PrepareToAdjustFirstColumnWidth()    
  6.  {    
  7.   this.ItemContainerGenerator.StatusChanged += (o, e) =>    
  8.    {    
  9.      if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)    
  10.     {    
  11.        if (this.Items.Count > 0)    
  12.        {    
  13.      var item = this.Items[this.Items.Count - 1];    
  14. var treeItem = this.ItemContainerGenerator.ContainerFromItem(item) 
  15. as TreeListViewItem;    
  16. treeItem.Loaded += (oo, ee) =>    
  17.    {    
  18.       this.AdjustFirstColumnWidth();    
  19.    };    
  20.     }    
  21.     }    
  22.     };    
  23.  }  

這樣,***一個(gè)孩子的可視內(nèi)容都加載好后,才會(huì)觸發(fā)調(diào)整寬度的代碼。

第二個(gè)問題比較簡(jiǎn)單,看了TreeListView的源碼后,發(fā)現(xiàn)它在TreeListViewItem類的模板中使用了GridViewRowPresenter類,然后為它定義了名字:“PART_Header”。在模板中以PART_起頭的控件是控件的約定,具體內(nèi)容見:《WPF Parts Control Model》。所以我可以使用以下方法找到它,而不用考慮新的模板是否有它:

  1. private TreeGridViewRowPresenter FindGridRow()    
  2. {    
  3. var rowPresenter = this.Template.FindName("PART_Header"this
  4. as TreeGridViewRowPresenter;    
  5.  return rowPresenter;    
  6. }   

要解決第三個(gè)問題,我們需要知道GridViewRowPresenter中如何生成一行,并知道***生成的控件結(jié)構(gòu)。先看看GridViewRowPresenter***生成的控件結(jié)構(gòu),這里我使用的是Snoop:

image

圖2 用Snoop查看TreeGridViewRowPresenter的可視化結(jié)構(gòu)

我們發(fā)現(xiàn),GridViewRowPresenter下只是簡(jiǎn)單的包含了幾個(gè)可視元素,它們剛好是每一列所顯示的內(nèi)容。再查看GridViewRowPresenter的源代碼,發(fā)現(xiàn)它擁有以下屬性:public GridViewColumnCollection Columns{get;set;}、internal UIElementCollection InternalCollection{get;set;},進(jìn)一步分析后,我猜測(cè)性地得出以下結(jié)論:GridViewRowPresenter.InternalCollection簡(jiǎn)單地包含了所有列的顯示元素,它會(huì)根據(jù)Columns屬性中各行對(duì)這些可視元素進(jìn)行維護(hù),讓它們顯示得跟表格一樣。

至此,第三個(gè)問題解決了:

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

***一個(gè)問題,是過程中最麻煩的一個(gè)問題。我們看到,圖2中該行下的***個(gè)元素是***列的顯示元素,顯示了“2.1”。但是文本左邊的Expander控件卻是TreeGridViewRowPresenter的***一個(gè)可視化孩子。而且縮進(jìn)并不是一個(gè)控件。那么這是怎么一回事呢?看了TreeGridViewRowPresenter的源碼后,發(fā)現(xiàn)原來是它主動(dòng)把Expander放在了***:

  1. public class TreeGridViewRowPresenter : GridViewRowPresenter    
  2. {    
  3.   protected override System.Windows.Media.Visual GetVisualChild(int index)    
  4.    {    
  5.      // Last element is always the expander    
  6.    // called by render engine    
  7.   if (index < base.VisualChildrenCount) return base.GetVisualChild(index);    
  8.  if (index == base.VisualChildrenCount) return this.lbRowNo;    
  9.  return this.Expander;    
  10.     }    
  11.   protected override int VisualChildrenCount    
  12.  {    
  13.   get   
  14.      {    
  15.         // Last element is always the expander    
  16.       if (this.Expander != null)    
  17.         return base.VisualChildrenCount + 2;    
  18.    else   
  19.       return base.VisualChildrenCount + 1;    
  20.    }    
  21.    }    
  22. }  

而文本前面先顯示縮進(jìn),然后再顯示Expander的原因是由于TreeGridViewRowPresenter類重寫了FrameworkElement.ArrangeOverride方法。在該方法中,它把***列的元素顯示的長(zhǎng)度變短在之前顯示一段縮進(jìn)的空白和Expander控件:

  1. protected override Size ArrangeOverride(Size arrangeSize)    
  2.  {    
  3.  Size s = base.ArrangeOverride(arrangeSize);    
  4. if (this.Columns == null || this.Columns.Count == 0) return s;    
  5. UIElement expander = this.Expander;    
  6. double current = 0;    
  7. double max = arrangeSize.Width;    
  8.  for (int x = 0; x < this.Columns.Count; x++)    
  9. {    
  10.   GridViewColumn column = this.Columns[x];    
  11.   // Actual index needed for column reorder    
  12.   UIElement uiColumn = (UIElement)base.GetVisualChild((int)ActualIndexProperty.GetValue(column, null));    
  13.  // Compute column width    
  14.   double w = Math.Min(max, (Double.IsNaN(column.Width)) ? (double)DesiredWidthProperty.GetValue(column, null) : column.Width);    
  15.  // First column indent    
  16.   if (x == 0 && expander != null)    
  17.   {    
  18.     double indent = FirstColumnIndent + expander.DesiredSize.Width;    
  19.    uiColumn.Arrange(new Rect(current + indent, 0, w - indent, arrangeSize.Height));    
  20.       }    
  21.      else   
  22.     {    
  23.       uiColumn.Arrange(new Rect(current, 0, w, arrangeSize.Height));    
  24.      }    
  25.   max -= w;    
  26.      current += w;    
  27.     }    
  28.   // Show expander    
  29.  if (expander != null)    
  30.     {    
  31.   expander.Arrange(new Rect(this.FirstColumnIndent, 0, expander.DesiredSize.Width, expander.DesiredSize.Height));    
  32.   }    
  33.   return s;    
  34. }  

分析到這里,就知道如何計(jì)算出***列的最終寬度了:

  1. private double GetFirstColumnDesiredWidth()    
  2.  {    
  3.   var rowPresenter = this.FindGridRow();    
  4.  if (VisualTreeHelper.GetChildrenCount(rowPresenter) <= 0) return 0;    
  5.  //GridViewRowPresenter中的每一個(gè)元素表示一列。    
  6.  var firstColumn = VisualTreeHelper.GetChild(rowPresenter, 0) as UIElement;    
  7. var desiredWidth = firstColumn.DesiredSize.Width;    
  8.  //需要的寬度前,需要加上列的縮進(jìn)和Expander的寬度。    
  9.  var indent = rowPresenter.FirstColumnIndent + rowPresenter.Expander.DesiredSize.Width;    
  10.   return indent + desiredWidth + ENSURE_SIZE;    
  11.  }  

加上以下這段代碼后,程序終于可以正確運(yùn)行了。

總結(jié)

解決這個(gè)問題,花了一天多的時(shí)間,主要原因還是因?yàn)閷?duì)WPF還是處在入門的級(jí)別。其中學(xué)到了以下內(nèi)容:

熟悉了TreeView、TreeViewItem、ItemsControl的使用及樹型控件的原理。

樹型表格控件TreeListView的設(shè)計(jì)過程(見之前的文章)。

熟悉了Measure的使用。

原文標(biāo)題:技術(shù)總結(jié):自動(dòng)擴(kuò)張WPF樹型表格列寬

鏈接:http://www.cnblogs.com/zgynhqf/archive/2010/08/05/1793405.html

 

責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2022-11-13 10:07:22

SpringSpringBoot

2021-09-01 08:58:15

項(xiàng)目 UTFailed

2025-01-16 08:52:45

2021-06-06 16:15:57

地區(qū)接口項(xiàng)目

2025-02-13 07:00:00

Dubbo-goJava服務(wù)端

2009-09-14 17:08:02

WebFormView

2020-06-09 08:06:31

RocketMQ消息耗時(shí)

2021-10-08 07:50:57

軟件設(shè)計(jì)程序

2017-10-10 15:14:23

BUGiOS 11蘋果

2014-02-26 09:13:39

2022-04-17 10:04:32

HerokuPaaSPorter

2014-12-17 09:40:22

dockerLinuxPaaS

2024-04-22 00:00:01

Redis集群

2015-08-24 10:07:13

程序員bug

2019-08-01 12:59:21

Bug代碼程序

2022-06-15 08:14:40

Go線程遞歸

2023-03-13 08:09:03

Protobuffeature分割

2022-05-16 08:42:26

Pandasbug

2024-08-08 08:09:38

2021-03-23 18:01:14

SQL數(shù)據(jù)庫(kù)前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 久久久国产精品视频 | 91色视频在线观看 | 精品精品 | 中文字幕在线一区 | 九九精品在线 | 伊人婷婷| 日韩视频一区二区在线 | 日本免费黄色一级片 | 国产精品久久久久一区二区三区 | 精品国产18久久久久久二百 | 久草福利| 欧美自拍第一页 | www.一区二区三区 | 色吧久久 | 欧美视频二区 | 国产在线高清 | 99免费视频| a久久久久久 | 欧美一级视频免费看 | 涩涩99| 成人免费小视频 | 精品久久久久久久 | 欧美一级在线 | 国产区视频在线观看 | 国产欧美精品 | 美日韩免费视频 | 九九九精品视频 | 精品在线播放 | 九九综合 | 久久久精品网站 | 国产精品99999 | 成人av播放 | 亚洲综合在线视频 | 成人精品福利 | 美女露尿口视频 | 精品国产乱码久久久久久丨区2区 | 国产ts人妖系列高潮 | 欧美一区二区三区视频 | 伦理二区 | 天天想天天干 | 国内精品久久久久久久影视简单 |