wpf設計時調試
編輯模型
裝飾器
1.wpf設計時調試
為了更好的了解wpf設計時框架,那麼調試則非常重要,通過以下配置可以調試控件的設計時代碼
(1)將啟動項目配置成外部的visual studio ide啟動程序devenv.exe
(2)F5啟動調試然後會打開一個新的visual studio ide,這個時候要記得重新打開你要調試的那個項目.
以上兩個步驟就可以實現設計時調試了
2.編輯模型體系
當選中某些控件出現的設計時,這個選中的控件便成了可編輯的對象. 設計環境會傳回一個ModelItem的類,這個類可以幫助你更改控件視圖,用於交互.這裡的做法與直接更改wpf控件有些不同,如直接改變wpf一個控件的一個屬性
this.Content = "Hello";
在設計環境下,也可以獲取到處在編輯狀態的控件,若采取上面的做法,雖可以變更控件的視圖,但卻未變更控件在xaml的更改.
設計環境下所有控件的更改都封裝在ModelItem類中.做法如下
//selectedContent's Type is ModelItem selectedContent.Properties["Content"].SetValue("Hello"); //selectedContent.Properties[ContentControl.ContentProperty].SetValue("Hello");
selectedContent類型是ModelItem,其中用Properties索引值獲取屬性,有兩種方式獲取屬性,字符串和靜態項屬性
在上篇介紹過控件上下文菜單的設計時做法,我們以此為基礎做個示例,以ContentControl為例,如下圖
共有4個操作,SetHello和SetRedForeground用於測試ModelItem變更控件視圖的功能,
第三個和第四個菜單用於測試變更時事務的操作.(即要麼全過,要麼全不過)
以下為詳細代碼
public class ContentMenuProvider : PrimarySelectionContextMenuProvider { //略去構造函數添加MenuAcion的代碼 void ModelEditingScopeFailMenuAction_Execute(object sender, MenuActionEventArgs e) { ModelItem selectedContent = e.Selection.PrimarySelection; using (ModelEditingScope scope = selectedContent.BeginEdit("test")) { //selectedContent's Type is ModelItem selectedContent.Properties["Content"].SetValue("TextModelEditingScope"); //wrong selectedContent.Properties["Foreground"].SetValue("Red"); scope.Complete(); } } void ModelEditingScopeMenuAction_Execute(object sender, MenuActionEventArgs e) { ModelItem selectedContent = e.Selection.PrimarySelection; using (ModelEditingScope scope = selectedContent.BeginEdit("test")) { selectedContent.Properties["Content"].SetValue("TextModelEditingScope"); selectedContent.Properties["Foreground"].SetValue(Brushes.Green); scope.Complete(); } } void SetRedForegroundMenuAction_Execute(object sender, MenuActionEventArgs e) { ModelItem selectedContent = e.Selection.PrimarySelection; selectedContent.Properties["Foreground"].SetValue(Brushes.Red); selectedContent.Properties[Control.ForegroundProperty].SetValue(Brushes.Red); } void SetHelloMenuAction_Execute(object sender, MenuActionEventArgs e) { ModelItem selectedContent = e.Selection.PrimarySelection; //ContentControl element = selectedContent.View as ContentControl; //selectedContent.Content = "hello"; selectedContent.Properties["Content"].SetValue("Hello"); } }
這裡ModelItem通過MenuActionEventArgs傳遞進來.當需要事務支持時,則需要用到ModelEditingScope對象,通過ModelItem的BeginEdit方法,當全部變更完成時,則調用ModelEditingScope的Complete方法.
這裡可以知道ModelItem是設計器對控件做出所有的變更的一個封裝,也是wpf控件設計器的一個基礎.上面介紹通過Properties索引值變更視圖,具體ModelItem其他的功能可以參考msdn相關文檔.
3.裝飾器
上圖是asp.net裡面GridView控件的一個設計視圖,右側的可視化面板到了wpf控件的設計時就稱為裝飾器,那麼這個裝飾器其實就是wpf的控件,實現可視化界面對於使用者而言非常重要,可以了解該控件的常用功能,也省卻了一些開發時間.不過目前wpf內置控件似乎沒一個控件是具有這一功能的,只能希望下版visual studio對wpf控件時做的更好了,可怕的是我們自己無法擴展內置控件的設計時,只能等著微軟來做,最可怕的是visual studio 2010把wpf設計時部分的api全變掉,那麼這裡就全白寫了.
裝飾器功能由AdornerProvider提供,我們從內置提供的PrimarySelectionAdornerProvider類派生一個裝飾器.
派生類需要重寫以上兩個方法,當選中該設計器相關控件時,會調用Activate方法,離開時調用Deactivate方法.為了介紹裝飾器的使用方法.示例將盡量簡單,以介紹裝飾器功能.
一般情況下,裝飾器需要一下幾個步驟
(1)定義一個AdornerPanel對象,並把控件相關裝飾器添加到AdornerPanel中,然後將這個裝飾器容器添加到AdornerProvider的Adorners屬性中,這項操作在Activate方法中完成.
protected override void Activate(ModelItem item, DependencyObject view) { Slider opacitySlider = new Slider(); opacitySlider.Background = Brushes.Red; AdornerPanel myPanel = new AdornerPanel(); myPanel.Children.Add(opacitySlider); Adorners.Add(myPanel); base.Activate(item, view); }
注意Activate方法也將ModelItem傳遞進來,就意味著通過裝飾器的方式也可以變更控件視圖
(2)定位裝飾器
再來看asp.net的GridView的設計時面板,其出現在控件的右側,wpf設計時裝飾器體系允許變更裝飾器位置,上面做的第一個步驟並無法在視圖上看到控件的裝飾器,那是因為裝飾器的定位問題.這個步驟必須完成.
這項工作由AdornerPlacementCollection對象來完成,再通過AdornerPanel的靜態方法SetPlacements來設置裝飾器的位置.
我們來看一下AdornerPlacementCollection的幾個方法,初看會比較暈,無法判斷幾個方法的區別
以上8個方法可以看做4個方法,都是與高度寬度有關的.下面以圖來說明,我們定義的控件裝飾器是一個Slider控件.目前只用於演示作用,它做不了什麼其他事情.
以上是原控件,裝飾器建立在此基礎之上,這裡說明一下上面8個方法的參數,都是一致的,第一個是倍數因子,第二個偏移量
(1)設置容器大小
為了演示,所以裝飾器控件以紅色背景標記,以下代碼添加在Activate方法後面,SizeRelativeToContentWidth && Height方法設置裝飾器容器的大小,設置寬度和高度為控件內容高度和寬度的1倍
相應代碼
AdornerPlacementCollection placement = new AdornerPlacementCollection(); placement.SizeRelativeToContentWidth(1, 0); placement.SizeRelativeToContentHeight(1, 0); AdornerPanel.SetPlacements(opacitySlider, placement);
(2)變更裝飾器位置(PositionRelativeToAdornerWidth方法),倍數參數為正數則向右移,負數則向左移
代碼變更為
AdornerPlacementCollection placement = new AdornerPlacementCollection(); placement.SizeRelativeToContentWidth(1, 0); placement.SizeRelativeToContentHeight(1, 0); placement.PositionRelativeToAdornerWidth(1, 0); AdornerPanel.SetPlacements(opacitySlider, placement);
(3)DesiredWidth和DesiredHeight,注意在設計時,控件與容器之間也有著間距,如下紅圈
SizeRelativeToAdornerDesiredWidth方法用於調整裝飾器的DesiredWidth.看下圖黃色紅圈的位置變更,裝飾器向右側移動一個DesiredWidth,並寬度添加一個DesiredWidth.
代碼變更為
AdornerPlacementCollection placement = new AdornerPlacementCollection(); placement.SizeRelativeToContentWidth(1, 0); placement.SizeRelativeToContentHeight(1, 0); placement.PositionRelativeToAdornerWidth(1, 0); placement.SizeRelativeToAdornerDesiredWidth(1, 0); AdornerPanel.SetPlacements(opacitySlider, placement);
注意PositionRelativeToAdornerWidth方法變更為PositionRelativeToContentWidth方法,那麼SizeRelativeToAdornerDesiredWidth方法只會使裝飾器寬度變更,而位置則不做變更.如下圖
代碼變更為
AdornerPlacementCollection placement = new AdornerPlacementCollection();
placement.SizeRelativeToContentWidth(1, 0);
placement.SizeRelativeToContentHeight(1, 0);
//placement.PositionRelativeToAdornerWidth(1, 0);
placement.PositionRelativeToContentWidth(1, 0);
placement.SizeRelativeToAdornerDesiredWidth(5, 0);
AdornerPanel.SetPlacements(opacitySlider, placement);
一邊情況下不設置容器高度,因為控件的高度是不確定的,所以現在調整如下
現在最終代碼變更為
AdornerPlacementCollection placement = new AdornerPlacementCollection();
placement.SizeRelativeToContentWidth(1, 0);
placement.PositionRelativeToAdornerWidth(1, 0);
placement.SizeRelativeToAdornerDesiredHeight(1, 0);
AdornerPanel.SetPlacements(opacitySlider, placement);
上面方法可以做多次嘗試,便於好的理解.