今天小編給大家分享一下自動(dòng)擴(kuò)張WPF樹(shù)型表格列寬問(wèn)題怎么解決的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
創(chuàng)新互聯(lián)公司擁有網(wǎng)站維護(hù)技術(shù)和項(xiàng)目管理團(tuán)隊(duì),建立的售前、實(shí)施和售后服務(wù)體系,為客戶(hù)提供定制化的網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、網(wǎng)站維護(hù)、簡(jiǎn)陽(yáng)服務(wù)器托管解決方案。為客戶(hù)網(wǎng)站安全和日常運(yùn)維提供整體管家式外包優(yōu)質(zhì)服務(wù)。我們的網(wǎng)站維護(hù)服務(wù)覆蓋集團(tuán)企業(yè)、上市公司、外企網(wǎng)站、商城網(wǎng)站定制開(kāi)發(fā)、政府網(wǎng)站等各類(lèi)型客戶(hù)群體,為全球上1000家企業(yè)提供全方位網(wǎng)站維護(hù)、服務(wù)器維護(hù)解決方案。
原問(wèn)題如下:
圖1 問(wèn)題描述
背景
樹(shù)型控件在GIX4系統(tǒng)中已經(jīng)被大量使用。這個(gè)控件是一年前其它同事在網(wǎng)上搜索到,再引入的。
一開(kāi)始的時(shí)候,要解決這個(gè)問(wèn)題,想到的最直接的方案是這樣的:找到***列中的Expander控件(加號(hào):),然后監(jiān)聽(tīng)它的“Expanded”事件;在事件處理程序中,計(jì)算所需要的寬度,然后設(shè)置為控件的寬度。
按照這個(gè)方案去實(shí)際寫(xiě)代碼時(shí),發(fā)現(xiàn)并沒(méi)有想象中那么簡(jiǎn)單,發(fā)現(xiàn)了很多問(wèn)題。例如,Expander并不是Expander控件,而是一個(gè)ToggleButton,而且是寫(xiě)在模板中的,TreeGridRowPresenter中的Expander的類(lèi)型也只是UIElement,也就是說(shuō),不能把Expander從UIElement轉(zhuǎn)換為T(mén)oggleButton,這樣程序會(huì)寫(xiě)得很死。又如,如何計(jì)算***列的所需要寬度。
這些問(wèn)題是要上面提及的BUG所需要解決的:
四個(gè)待解決的問(wèn)題
1. 何時(shí)觸發(fā)是最合適的?在何處觸發(fā)調(diào)整寬度的代碼?
2. 如何找到樹(shù)型控件的所有GridViewRowPresenter。
3. GridViewRowPresenter中,如何把***列的控件找到。
4. ***列控件的組成結(jié)構(gòu)是怎么樣的,它所需要的大小如何求出,是否可以直接使用Measure和DesiredSize。
一步一步解決
***個(gè)問(wèn)題,何時(shí)觸發(fā)這個(gè)功能?其實(shí)我是要在點(diǎn)擊后,當(dāng)子節(jié)點(diǎn)都加載好后,然后計(jì)算出合適的大小,再設(shè)置給列對(duì)象。我先在TreeListView的OnExpanded事件處理程序中嘗試編寫(xiě)代碼獲取每一個(gè)TreeListView,但是發(fā)現(xiàn)這個(gè)事件在發(fā)生時(shí),所有的子節(jié)點(diǎn)并沒(méi)有生成,所以不能通過(guò)ItemContainerGenerator.GetContainerForItem方法獲取到窗口,此方案失敗。接著,我查看了ItemsControl的接口聲明,發(fā)現(xiàn)ItemContainerGenerator屬性有事件StatusChanged。所以我就改為監(jiān)聽(tīng)這個(gè)事件,并判斷如果當(dāng)它的Status變?yōu)镃ontainersGenerated時(shí),就表示所有子節(jié)點(diǎn)已經(jīng)生成了。代碼如下:
this.ItemContainerGenerator.StatusChanged += (o, e) => { if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { this.AdjustFirstColumnWidth(); } };
但是同樣發(fā)現(xiàn)新的問(wèn)題,這時(shí)候雖然窗口對(duì)象TreeListView已經(jīng)生成,但是它下面的所有Visual Child都沒(méi)有生成,這樣同樣無(wú)法獲取到它里面用來(lái)顯示每一行的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(); }; } } }; }
這樣,***一個(gè)孩子的可視內(nèi)容都加載好后,才會(huì)觸發(fā)調(diào)整寬度的代碼。
第二個(gè)問(wèn)題比較簡(jiǎn)單,看了TreeListView的源碼后,發(fā)現(xiàn)它在TreeListViewItem類(lèi)的模板中使用了GridViewRowPresenter類(lèi),然后為它定義了名字:“PART_Header”。
private TreeGridViewRowPresenter FindGridRow() { var rowPresenter = this.Template.FindName("PART_Header", this) as TreeGridViewRowPresenter; return rowPresenter; }
要解決第三個(gè)問(wèn)題,我們需要知道GridViewRowPresenter中如何生成一行,并知道***生成的控件結(jié)構(gòu)。先看看GridViewRowPresenter***生成的控件結(jié)構(gòu),這里我使用的是Snoop:
圖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è)問(wèn)題解決了:
var firstColumn = VisualTreeHelper.GetChild(rowPresenter, 0) as UIElement;
***一個(gè)問(wèn)題,是過(guò)程中最麻煩的一個(gè)問(wèn)題。我們看到,圖2中該行下的***個(gè)元素是***列的顯示元素,顯示了“2.1”。但是文本左邊的Expander控件卻是TreeGridViewRowPresenter的***一個(gè)可視化孩子。而且縮進(jìn)并不是一個(gè)控件。那么這是怎么一回事呢?看了TreeGridViewRowPresenter的源碼后,發(fā)現(xiàn)原來(lái)是它主動(dòng)把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類(lèi)重寫(xiě)了FrameworkElement.ArrangeOverride方法。在該方法中,它把***列的元素顯示的長(zhǎng)度變短在之前顯示一段縮進(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; }
分析到這里,就知道如何計(jì)算出***列的最終寬度了:
private double GetFirstColumnDesiredWidth() { var rowPresenter = this.FindGridRow(); if (VisualTreeHelper.GetChildrenCount(rowPresenter) <= 0) return 0; //GridViewRowPresenter中的每一個(gè)元素表示一列。 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; }
以上就是“自動(dòng)擴(kuò)張WPF樹(shù)型表格列寬問(wèn)題怎么解決”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。