溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

自動擴張WPF樹型表格列寬問題怎么解決

發(fā)布時間:2022-01-11 10:14:43 來源:億速云 閱讀:188 作者:iii 欄目:編程語言

今天小編給大家分享一下自動擴張WPF樹型表格列寬問題怎么解決的相關(guān)知識點,內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

原問題如下:

自動擴張WPF樹型表格列寬問題怎么解決

圖1 問題描述

背景

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

一開始的時候,要解決這個問題,想到的最直接的方案是這樣的:找到***列中的Expander控件(加號:自動擴張WPF樹型表格列寬問題怎么解決),然后監(jiān)聽它的“Expanded”事件;在事件處理程序中,計算所需要的寬度,然后設(shè)置為控件的寬度。

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

這些問題是要上面提及的BUG所需要解決的:

四個待解決的問題

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

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

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

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

一步一步解決

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

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

但是同樣發(fā)現(xiàn)新的問題,這時候雖然窗口對象TreeListView已經(jīng)生成,但是它下面的所有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();       };        }        }        };     }

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

第二個問題比較簡單,看了TreeListView的源碼后,發(fā)現(xiàn)它在TreeListViewItem類的模板中使用了GridViewRowPresenter類,然后為它定義了名字:“PART_Header”。

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

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

自動擴張WPF樹型表格列寬問題怎么解決

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

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

至此,第三個問題解決了:

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

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

而文本前面先顯示縮進(jìn),然后再顯示Expander的原因是由于TreeGridViewRowPresenter類重寫了FrameworkElement.ArrangeOverride方法。在該方法中,它把***列的元素顯示的長度變短在之前顯示一段縮進(jìn)的空白和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;     //需要的寬度前,需要加上列的縮進(jìn)和Expander的寬度。     var indent = rowPresenter.FirstColumnIndent + rowPresenter.Expander.DesiredSize.Width;      return indent + desiredWidth + ENSURE_SIZE;     }

以上就是“自動擴張WPF樹型表格列寬問題怎么解決”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

wpf
AI