本文章是由機器翻譯。

UI 最前線

跳脫 Grid 的思維

Charles Petzold

下載程式碼範例

在畫布是 Windows Presentation Foundation (WPF) 和 Silverlight 中, 可用的幾個版面配置選項的其中一個,而且 ’s 最穩固地根目錄在傳統的一個。當在畫布填滿兒童您位置藉由指定每個子系協調使用 Canvas.Left 和 Canvas.Top 附加屬性。這是相當不同典範從其他面板,其中排列子元素根據簡單的演算法,而不需任何需要的程式設計人員找出實際的位置。

當您聽到 「 畫布 」 這個字時,您可能認為有關繪製和繪圖。基於該原因也許,使用 WPF 和 Silverlight 的程式設計人員傾向於在畫布 relegate 至向量圖形顯示。還,使用 [畫布線條]、 [聚合線條]、 [多邊形] 和 [路徑的項目顯示時元素本身就會包括將它們放置在畫布內的座標點。如此一來您 don’t 需要擔心Canvas.Left 和 Canvas.Top 附加的屬性。

所以為何畫布如果 don’t 需要使用它所提供的附加的屬性?有一個更好的方法嗎?

畫布對。格線

這些年來我有逐漸傾向拒絕 gravitating 改向單一儲存格格線使用來顯示向量圖形畫布。單一儲存格的格線,就如同以外沒有任何資料列或資料行定義的一般格線。如果 Grid 具有只能有一個儲存格,您可以將多個項目放入方格儲存格並 don’t 無論使用何種 [格線附加屬性來表示資料列或欄。

一開始,使用畫布或單一儲存格格線似乎很類似。無論如何哪一種您使用的向量圖形線條,聚合線條、 多邊形和路徑項目明細欄相對於根據其座標點容器的左上角。

在 [畫布] 和 [單一儲存格格線之間的差異在於容器顯示其餘的配置系統的方式。WPF 和 Silverlight 合併兩段式資料、 由上而下配置每個項目位置 interrogates 及其子系的大小,然後會負責排列其相對於本身的子系。這個版面配置] 系統中 [畫布] 和 [單一儲存格格線是很大的差異:

  • 子系 Grid 具有作為自己的父維度相同的維度。這些是通常有限的維度,但是在畫布永遠會出現有無限的維度至其子系。
  • Grid 報告其子系的複合大小到它的父代。不過,在畫布永遠有明顯的大小為零不管它包含的子系。

假設您有一大堆形成某種卡通化類似向量圖形影像的多邊形項目。如果所有這些多邊形項目置於單一儲存格格線格線大小會根據多邊形的最大的水平和垂直座標。格線可以再被視為一個一般有限大小內的項目配置系統因為它的大小正確地反映出複合影像的大小。(實際上,這正確只有在使用影像的左上角是在點 (0,0),且存在數都沒有負的座標)。

將所有這些多邊形放在一個畫布不過,並在畫布報告到版面配置系統它具有大小為零。在一般時將複合向量圖形影像整合至您的應用程式,您幾乎可以確定可較快得到想單一儲存格的格線,而不是在畫布的行為。

所以在畫布是完全無用的嗎?完全不會!訣竅是使用您利用到畫布 peculiarities。在某種非常真正的意義在畫布 doesn’t 參與版面配置。因此,您可以使用它需要超越版面配置時 — 顯示中斷的配置系統和其外部的 float 界限的圖形。依預設在畫布 doesn’t 因此甚至如果是非常小它仍然可以裝載其界限外的子系剪輯其子系。在畫布是多比容器顯示項目或圖形的某一參考點。

在畫布很棒的技巧我前來視為 「 思考外 Grid 」。雖然我顯示程式碼範例在 Silverlight 中,您可以在 WPF 中使用相同的技術。伴隨著本文的可下載來源程式碼 Visual Studio 方案,名為 ThinkingOutsideTheGrid,而且您可以播放與在 程式charlespetzold.com/silverlight/ThinkingOutsideTheGrid.

視覺化連結控制項

假設您 Silverlight 中有一大堆控制項或 WPF 應用程式和您需要提供某種的兩個或多個控制項之間的視覺連結。您想要從一個控制項繪製一條線到另一個的也許是而且也許這一行會之間交互其他控制項。

當然這一行必須中變更作出反應版面配置,或許是與視窗或使用者調整頁面大小。更新版面配置時被通知是絕佳 LayoutUpdated 事件的應用程式 — 事件我絕對不會有場合使用之前探索我在本文中說明的問題。藉由在 WPF 中 UIElement 以及 FrameworkElement 在 Silverlight 中,被定義 LayoutUpdated。如名稱建議,配置階段已經重新排列在螢幕上的項目後引發事件。

在處理 LayoutUpdated 事件時您 don’t 想要執行任何動作會導致另一個配置階段並取得您 embroiled 中無限遞迴。’s 位置在畫布進來方便:因為它永遠會報告到它的父代為零大小,您可以改變在畫布中的項目而不會影響到版面配置。

XAML 檔案 ConnectTheElements 程式的結構如下:

<UserControl ... >
  <Grid ... >
    <local:SimpleUniformGrid ... >
      <Button ... />
      <Button ... />
      ...
    </local:SimpleUniformGrid>

    <Canvas>
      <Path ... /> 
      <Path ... />
      <Path ... />
    </Canvas>
  </Grid>
</UserControl>

Grid 包含計算資料列和資料行以顯示依據其整體大小及長寬比及其子系數目的 SimpleUniformGrid。 當您變更視窗的大小,資料列和資料行的號碼將會有所變更,儲存格將會轉換周圍。 在這個 SimpleUniformGrid 32 的按鈕的兩個按鈕有 btnA 和 btnB 的名稱。 在畫布佔用 [SimpleUniformGrid 為相同的區域,但位在它的上方。 此畫布包含程式用來繪製兩個具名的按鈕和一條線周圍的省略它們之間的路徑項目。

程式碼後置檔案做 LayoutUpdated 事件期間了其所有的工作。 它需要尋找兩個具名按鈕相對於畫布可以方便地也與對齊將 SimpleUniformGrid、 格線和 MainPage 本身的位置。

若要在相同的視覺化樹狀結構中找到的任何項目相對於任何其他項目的位置,使用 TransformToVisual 方法。 這個方法定義依 Visual 類別在 WPF 中和在 Silverlight,UIElement,但它在兩個環境中運作方式相同。 假設元素 el1 是 el2 所佔用的區域內的某處。 (在 ConnectTheElements,el1 是一個按鈕和 el2 是 MainPage])。這個方法呼叫傳回型別是抽象的父類別到所有其他圖形轉換類別的 GeneralTransform 的物件:

el1.TransformToVisual(el2)

您 can’t 真正做任何處理 GeneralTransform 除了呼叫會轉換到另一個點從一個座標空間的其轉換] 方法。

假設您想要尋找中心 el1 但 el2 ’s 座標空間中。 請看看下面這個程式碼:

Point el1Center = new Point(
  el1.ActualWidth / 2, el1.ActualHeight / 2); 
Point centerInEl2 = 
  el1.TransformToVisual(el2).Transform(el1Center);

如果 el2 任一個畫布,或對齊在畫布您可以再使用該 centerInEl2 點來設定圖形表面上將會在畫布中位於 el1 的中央。

ConnectTheElements 來繪製兩個的具名按鈕周圍的省略其 WrapEllipseAroundElement 方法中執行這個轉換,,然後計算一行之間省略符號按鈕的中心之間線條的交集為基礎的座標。結果如 [圖 1] 所示。

圖 1ConnectTheElements 顯示

如果您嘗試在 WPF 中的此程式,[SimpleUniformGrid 變更為更動態的變更版面配置中 WrapPanel,您在調整大小時程式 ’s 視窗。

追蹤滑桿

變更圖形和其他視覺效果,以回應捲軸或滑桿中所做的變更是非常基本的然後在 WPF 和 Silverlight 您可以在程式碼或 XAML 的繫結中操作。但您想要對齊圖形完全與實際的滑動軸捲動方塊的要是嗎?

這是我 conceived 為互動式三角函數示範的型別 TriangleAngles 專案背後概念。我排列兩個滑桿、 一個垂直和彼此一律為直角的一個水平。所示兩個滑桿大拇指會定義兩個頂點的直角三角形圖 2.

圖 2TriangleAngles 顯示

請注意如何半透明三角形位於上方的兩個滑桿。當您在移動滑桿大拇指三角形的邊變更圖片大小和比例,如同題字用的角度和標籤上的垂直和水平的雙腿所示。

這是複雜性的一個畫布覆疊,但增加層很明顯的另一項工作,因為程式需要取得存取權] 滑桿捲動方塊。該滑動軸捲動方塊是控制項範本的一部份:大拇指會指定在樣板內的名稱,但不幸的是外範本 can’t 存取這些名稱。

而是,經常不可或缺的 VisualTreeHelper 靜態類別談到救。這個類別可讓您查核行程 (或而爬) 在 WPF 或 Silverlight GetParent、 GetChildenCount 和 GetChild 方法透過任何視覺樹狀結構。若要到一般化的尋找特定的型別子程序,我寫了一點遞迴的泛型方法:

T FindChild<T>(DependencyObject parent) 
  where T : DependencyObject

我稱之為像這樣:

Thumb vertThumb = FindChild<Thumb>(vertSlider);
Thumb horzThumb = FindChild<Thumb>(horzSlider);

在該點我可以在兩個大拇指上使用 TransformToVisual,取得其相對於畫布重疊的座標。

嗯,它的一個滑桿,但不是另,工作和我一段時間,以恢復為滑動軸控制項範本會包含 共花費兩個 大拇指 — 一個用於水平方向,一個用於垂直。 方向為滑動軸設定,視半範本項目有可見性] 屬性設定為 [摺疊。 我加入 FindChild 方法的第二個引數呼叫 mustBeVisible 和用來放棄任何子系] 分支下的搜尋位置的項目是看不見的。

形成三角形的多邊形上設為 False,則 HitTestVisible 幫助避免干擾滑動軸捲動方塊的滑鼠輸入。

捲動 [ItemsControl 外

假設您與一個 DataTemplate 使用一個 ItemsControl 或清單方塊,以顯示控制項 ’s 集合中的物件。 可以在包含一個畫布該 DataTemplate 所以有關特定項目可以顯示在控制項外,但似乎捲動來追蹤項目作為控制項吗?

我 haven’t 發現這麼精確地的好方法。 大的問題似乎是 [ScrollViewer 所加諸的裁剪區域。 這個 ScrollViewer 剪輯剛好 dangle 其界限及因此該畫布上的任何項目以外的任何畫布。

不過,ItemsControl 內部運作有點額外知識,您可以執行一些動作接近至想要的是什麼。

我認為它 ’s 亦適用於一個 ItemsControl 中的項目,但實際上取出超出 ItemsControl 本身的單元,讓一個快顯不足為這項功能。 ItemsControlPopouts 專案示範的技巧。 若要提供 ItemsControl 若要顯示的項目,我建立了稱為位於資料子目錄的 ClientBin ProduceItems.xml 小資料庫。 ProduceItems 所組成的元素數目的每一個都包含名稱屬性的 ProduceItem 標記名稱,參考的項目,並將會是上一個選擇性訊息的點陣圖圖片的相片屬性會顯示 「 取出-不足 」 的 [ItemsControl。 (相片和其他作品是 Microsoft Office 美工圖案)。

ProduceItem 和 ProduceItems 類別提供程式碼支援 XML] 檔案並 ProduceItemsPresenter 讀取 XML 檔案,並將它還原序列化成 ProduceItems 物件。 這會設定的視覺化樹狀結構,其中包含 ScrollViewer 和 ItemsControl DataContext 屬性。 ItemsControl 包含簡單 DataTemplate 顯示項目。

現在您可能會偵測到問題有點。 程式有效地將型別 ProduceItem 的商務物件插入 [ItemsControl。 在內部 [ItemsControl 正在建立的每個項目根據 [DataTemplate 視覺化樹狀結構。 若要追蹤這些項目移動您需要存取該項目 ’s 內部視覺化樹狀結構來算出完全項目是相對於其他的程式。

此資訊可供使用。 ItemsControl 定義一個名為傳回的物件型別 ItemContainerGenerator ItemContainerGenerator 僅取得的屬性。 這是負責產生與 [ItemsControl 中的每個項目相關的視覺化樹狀目錄類別而且它包含很方便的方法,例如提供容器 (也就是實際上 ContentPresenter) 的 ContainerFromItem 控制項中的每個物件。

像是兩個其他程式,ItemsControlPopouts 程式涵蓋整個頁面與一個畫布。 再一次 LayoutUpdated event 可讓程式檢查是否畫布上的東西需要被改變。 LayoutUpdated 處理常式,此程式中的透過 ProduceItem 物件中 [ItemsControl 列舉,並檢查有非 Null 及非空白的郵件屬性。 每個這些訊息的屬性應該會對應到型別 PopOut 在畫布中的物件。 PopOut 是只是小型類別衍生自 ContentControl 與範本一起,以顯示一條線和訊息文字。 如果找不到 [PopOut,它建立,並在畫布中加入。 如果是存在只是重複使用它。

然後在畫布內必須位於 [PopOut。 程式會取得容器,對應至資料物件,並轉換它相對於畫布的位置。 如果該位置是在 [頂端] 和 [底部的 [ScrollViewer 之間 [PopOut 有可見性] 屬性設定為 [看得見。 否則 [PopOut 是隱藏的。

中斷超出儲存格

WPF 和 Silverlight 肯定有指定的方便性很棒的禮物版面配置中。 [格線] 和 [其他面板整齊地將項目放在儲存格,並確定該 ’s 它們保持。 如果您再假設便利性已放入項目,您需要的地方自由的必要限制將會以行軍。

Charles Petzold   是要 longtime 造成這個現象編輯器 MSDN 雜誌*.* 他最新的活頁簿是 「 的 Annotated Turing:A 引導式穿透 Alan Turing ’s 歷史紙張 Computability 和 Turing 機器上的教學課程 」 (Wiley,2008)。 他的網站上 Petzold 部落格 charlespetzold.com.

多虧來檢閱本文的下列的技術專家:Arathi Ramani 和 WPF 版面配置小組