原文連結: Silverlight Design Time Extensibility --By Justin Angel (Microsoft Silverlight Toolkit Program Manager)
嗨夥計們,
隻要有人談到開發者與設計師在 Silverlight/WPF上協同工作時,他們就會談論“設計,開發工作流程”這個
問題。即使您是您自己的設計師,這工作也始終是永遠存在于當你在“設計師”和“開發”之間切換“帽子”的過程中。
我是一個使用工具建立使用者界面的支援者。 我的生活讓我不能了解為什麼有人會選擇非産能(non-productive)
和手寫XAML的事情。
你能找出的一個情況就是當你使用(Expression Blend & Visual Studio WPF/Silverlight Designer)這類工
具進行工作時,如果使用正确,這些工具會對提高生産力起到巨大的推動作用。然而,這篇文章不是關于如何使用
這類工具,而是關于如何幫助那些使用您的控件作為工具進行設計的人。本文是關于開發者着手去讓設計師有更容
易的(設計)體驗并減少摩擦。
控件提供商和開發者通常都想給自己的控件以更好的體驗。然而,在這個問題上其缺乏大量的資訊。我決定用
本文糾正這種情況。
本文也與那些在項目中有設計師一起工作的開發者有關。我建立的解決方案可在這裡下載下傳, SilverlightControls.zip
介紹:
首先,我們會考慮在設計時的Assemblies工作。
考慮下面這個類:
public class myControl : Control
{
public string MyStringProperty { get ; set ; }
}
現在,考慮下面這個設計時屬性的類:
[Description( " I am a control " )]
public class myControl : Control
{
[Description( " I am a property " )]
public string MyStringProperty { get ; set ; }
}
在設計時assemblies 工作的方式就是将設計時屬性與實際的類進行分離。之後我們的類會是這個樣子:
public class myControl : Control
{
public string MyStringProperty { get ; set ; }
}
并且在我們的設計時組裝過程時,代碼相當于:
AddCallback( typeof (myControl), builder =>
builder.AddCustomAttributes( new DescriptionAttribute( " I am a control 2 " )));
AddCallback( typeof (myControl), builder =>
builder.AddCustomAttributes( " MyStringProperty " , new DescriptionAttribute( " I am a property " )));
在沒有研究太多API的情況下,可以看到第一行會顯示“myControl has the following DescriptionAttribute”,
第二行顯示:“myControl.MyStringProperty has this DescriptionAttribute”.
我們從運作時代碼中分離出設計時代碼. 下面讓我們看一下在BLEND中,它的樣子:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL2Y0QzADOyYzXi1WdoR3XldWYtl2LcxWZn5WYulGdzVnavw1cn9Gbi9CX0VmbuQHanlGbyVmdsl2cvw1LcpDc0RHaiojIsJye.png)
這一做法有哪些優勢?
1.将設計時代碼與運作時代碼解耦有助于創造更好的代碼。 (分離的問題,POCO,單一的責任等)
2.基于工具修改設計時屬性。這種做法允許支援不同的設計時屬性,而這些屬性基于它們運作的工具。
3.設計時變更無須重新編譯運作時元件。
4.不是運作時組裝的author可以添加設計時屬性。
5.進階的設計時支援,并不需要我們使用GUIDs注冊Visual Studio軟體包。
參考架構
這裡有一些步驟. 從本質上講,我們要建立以下結構:
一點都不亂( 譯者注:我看有點亂,呵呵),是吧?簡單地說,這僅僅是參考模型。
我們将建立3個新項目。
*. Design.dll -将包含設計時屬性,這些屬性用于Visual Studio WPF/Silverlight設計器和Expression Blend。
*. VisualStudio.design.dll -将包含設計時的功能,僅用于Visual Studio。
*. Expression.design.dll -将包含設計時的功能,僅用于Expression blend。
這種命名約定是強制性的。
第一步是增加項目引用,我們會在該項目中添加設計時支援用于設計時元件:
添加對共享設計時dll的引用:
并添加Blend design-time 引用:
步驟:
1. 添加一個Silverlight 類庫,該類庫會包含我們的控件. 我們将它命名為:SilverlightControls.
2. 添回一個myControl 類.
public class myControl : Control
{
public string MyStringProperty
{
get { return GetValue(MyStringPropertyProperty) as string ; }
set { SetValue(MyStringPropertyProperty, value); }
}
public static readonly DependencyProperty MyStringPropertyProperty =
DependencyProperty.Register( " MyStringProperty " , typeof ( string ), typeof (myControl), null );
}
3. 建立共享運作時DLL. 因為控件被命名為“SilverlightControls” ,是以這裡命名為“SilverlightControls.Design”.
這裡選擇類型為“WPF Custom Control library” 因為稍後會使用一些WPF 特性.如果喜歡,我們可以手工添加引用到普通類庫項目中。
4. 在“SilverlightControls.Design” 項目中添加對“SilverlightControls” 項目的引用, 以及Microsoft design time 元件和
Silverlight System.Windows.dll.
(Silverlight 2 引用路徑– 預設位于 c:"Program Files"Microsoft SDKs"Silverlight"v2.0"Reference Assemblies)
這是我們引用的元件:
5. 建立visual studio 設計時DLL.
因為要添加的設計時用于“SilverlightControls” 是以這裡命名為:“SilverlightControls.VisualStudio.Design”.
6. 在項目“SilverlightControls.VisualStudio.Design” 中添加對“SilverlightControls”的引用, 以及
Microsoft design time assemblies 和 Silverlight System.Windows.dll.
這裡我們添加的引用:
7. 建立expression blend 設計時 DLL.
因為要添加的設計時用于“SilverlightControls” 是以這裡命名為“SilverlightControls.Expression.Design”.
8. 在項目“SilverlightControls.Expression.Design” 中添加對“SilverlightControls” 的引用,Microsoft
design time assemblies 和 Blend specific dlls。
這裡我們添加的引用:
9. 我們需要在三個運作時元件項目中分别添加一個類,該類實作了IReigsterMetadata 接口.
我在這三個項目中使用下面模版.
public class MetadataRegistration : IRegisterMetadata
{
private static AttributeTable _customAttributes;
private static bool _initialized;
public void Register()
{
if ( ! _initialized)
{
MetadataStore.AddAttributeTable(CustomAttributes);
_initialized = true ;
}
}
public static AttributeTable CustomAttributes
{
get
{
if (_customAttributes == null )
{
_customAttributes = new CustomMetadataBuilder().CreateTable();
}
return _customAttributes;
}
}
private class CustomMetadataBuilder : AttributeTableBuilder
{
public DeveloperMetadataBuilder()
{
// TODO: Add Design time code here!
}
private void AddTypeAttributes(Type type, params Attribute[] attribs)
{
base .AddCallback(type, builder => builder.AddCustomAttributes(attribs));
}
private void AddMemberAttributes(Type type, string memberName, params Attribute[] attribs)
{
base .AddCallback(type, builder => builder.AddCustomAttributes(memberName, attribs));
}
}
}
我不想深入研究這個類做了什麼,以及IRegisterMetadata 和 AttributeTableBuilder 是什麼, 您可以到
MSDN 上去查找. 我隻想說,它們是extensibility framework 中的類,通過它們,我們可以對我們的類添加設
計時支援。并且,我也不打算深入分析我的 template 做了什麼. 起碼它確定 Metadata 僅被建立一次,并且提
供了簡單的通路方法,這些方法我們會在本DEMO中到處使用。
好,這就是我們目前的項目結構:
注:我們在每個項目中都已有了一個metadata 類.
10. 安裝設計時檔案拷貝。
設計時DLL檔案須與運作時DLL檔案放在相同的目錄下.
我喜歡自動完成相關的*design.dll 檔案拷貝. 你可以手工完成這一操作。
我使用的自動方式是添加一條 post-build action 到每個設計時項目中.
右擊我們的項目檔案 –> “屬性” –> “生成事件”. 粘帖下面内容到Post Build Events:
copy " $(TargetPath) " " $(SolutionDir) " SilverlightControls " Bin " Debug "
在所有的三個設計時項目中重複這一步驟.
建立我們的測試項目
1. 我們使用BLEND建立一個新的項目來使用我們的DLL. 我們會使用該項目來預覽我們的設計時改進。
2. 添加對“SilverlightControls.dll”的引用.
這時發生了什麼?
我們有了兩個設計時dll限制. 因為這兩個限制會被工具自動加載到tools:
Design time DLLs 檔案與 runtime DLL在一起.
Design time DLLs 遵守命名協定.
<RuntimeName>.Design.dll – 共享設計時
<RuntimeName>.Expression.Design.dll – 用于expression blend 設計時
<RuntimeName?.VisualStudio.Design.dll – 用于Visual studio wpf/silverlight 設計器
現在我們有了這個基本架構,接下來就開始用一下吧.
( 注:原文作者在這裡好像就做了一個章節,下面是後續的文章内容:屬性講解)
DescriptionAttribute (Shared)
Description 是一個讓我們在設計時工具中顯示類似智能說明的方法. 該屬性用于在類和屬性之間進行工作。
當我們說某個屬性是“Shared” 時,它意味着工作在兩個工具之間并且須放在共享設計時 dll中.
盡管目前 Visual studio 有隻讀的設計時界面, 一旦其獲得一個可以編輯的設計界面,這些都會在兩個工具間被支援.
那就讓我們在SilverlightControls.Design.MetadataRegistration類型中DescriptionAttribute.
public CustomMetadataBuilder()
{
AddTypeAttributes( typeof (myControl),
new DescriptionAttribute( " I am a control " ));
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ));
}
現在編譯我們的應用并在Expression Blend看一下這些描述。
DisplayNameAttribute (shared)
顯示名稱屬性用于顯示類型成員的(friendlier)名稱.
AddMemberAttributes(typeof(myControl),"MyStringProperty",
new DescriptionAttribute("I am a property"),
new DisplayNameAttribute("My String Property"));
下面是其在 Blend中的顯示:
CategoryAttribute (shared)
每個屬性都有一個預設分類. Blend 為我們的控件定義如下分類:
預設情況下,我們的屬性添加到了“Miscellaneous” 分類. 如果想重新定義它,我們可讓它在别處。
我們可能想讓我們的屬性添加到已有的分類中,比如“Common Properties”.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " Common Properties " ));
在Blend中,我們看到它被移動到了那個分類下:
我們可能想去建立我們自己在分類,該分類我們稱之為“My Category”.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ));
Blend 甚至可以在一個相同的自定義分類中進行屬性分組(group properties).
讓我們在控件中添加一個新的Integer 類型屬性:
public class myControl : Control
{
public string MyStringProperty
{
get { return GetValue(MyStringPropertyProperty) as string ; }
set { SetValue(MyStringPropertyProperty, value); }
}
public static readonly DependencyProperty MyStringPropertyProperty =
DependencyProperty.Register( " MyStringProperty " , typeof ( string ), typeof (myControl), null );
public int MyIntProperty
{
get { return ( int )GetValue(MyIntPropertyProperty) ; }
set { SetValue(MyIntPropertyProperty, value); }
}
public static readonly DependencyProperty MyIntPropertyProperty =
DependencyProperty.Register( " MyIntProperty " , typeof ( int ), typeof (myControl), null );
}
我們将它添加到我們自定義的分類中.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ));
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new CategoryAttribute( " My Category " ));
我們會看到在‘My Category’中有兩個屬性:
BrowsableAttribute (shared)
Browsable 屬性允許我們隐藏一些屬性,這些屬發現在Blend’的資料面闆(Data pane)或Visual
Studio的屬性視窗中不再有效.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ));
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new CategoryAttribute( " My Category " ),
BrowsableAttribute.No);
當我們在 Blend運作目前應用時,會看到在Data Pane中不在有該屬性了:
我們能夠使用 browsable 屬性隐藏一些繼承的屬性以及我們自定義的屬性.
EditorBrowsableAttribute (Blend)
我僅在Blend 設計時項目中标記了這個屬性, 但這是出于我對Visual Studio Silverlight 設計器的猜測.
這可能在VS的一些流行特征中支援, 但我建議在使用之前先考慮它。Editor Browsable 進階狀态可以讓你在
Blend 中的一個分類下隐藏一些屬性,除非使用者喜歡看到這些。基本上,隐藏一下較少使用的屬性可以讓分類
顯示更加清楚。
讓我們打開expression blend metadata 項目中的中繼資料檔案并添加下面設計時:
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new EditorBrowsableAttribute(EditorBrowsableState.Advanced));
這是Blend 顯示的分類:
我們會在該分類上看到一個小的“隐藏”箭頭:
如果點選它, 會展開并顯示該分類下的所有進階屬性:
如果使用者離開控件然後在選中它時,該分類顯示将再次關閉:
那我們在這裡假設直到你需要時我們在顯示它.
在Silverlight Framework 屬性裡在什麼地方應用該屬性呢? 很多地方.
下面是在Silverlight的“Layout” 分類中:
然後我點選它:
我們明白了為什麼要在同一分類下避免顯示太多的屬性.
TypeConverterAttribute (Shared)
指定用作此屬性所綁定到的對象的轉換器的類型。
起初, 它被用在從UI到背景模型的值轉換操作. 它在Blend 2 SP1 在現在還無法使用.
如果能使用就好了, 因為它有一些不錯的東西.
在Blend TypeConverters能做的一件事是支援标準值,我們會在操作中看到.
在我們的類似對象“MyObject”添加另一個屬性:
public class myControl : Control
{
public string MyStringProperty
{
…
}
public int MyIntProperty
{
…
}
public object MyObjectProperty
{
get { return ( object )GetValue(MyObjectPropertyProperty); }
set { SetValue(MyObjectPropertyProperty, value); }
}
public static readonly DependencyProperty MyObjectPropertyProperty =
DependencyProperty.Register( " MyObjectProperty " , typeof ( object ), typeof (myControl), null );
}
這是該屬性的預設設計時:
我們通過建立一個簡單的TypeConverter開始:
public class myTypeConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true ;
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection( new string [] { " Hello " , " World " , " foo " , " bar " });
}
}
該類型converter 的工作就是對任何使用該converter的屬性寫入相應的(可能)值(以代碼方式).
下面在共享設計時metadata中,我們加入了對該 TypeConverter的引用:
AddMemberAttributes( typeof (myControl), " MyObjectProperty " ,
new CategoryAttribute( " My Category " ),
new TypeConverterAttribute( typeof (myTypeConverter)));
然後我們在 Blend應用它:
我們得到了一系列可供選擇的值(以代碼方式).
同樣,我們可以使用内置式的TypeConverters, 比如 Boolean Converter:
AddMemberAttributes( typeof (myControl), " MyObjectProperty " ,
new CategoryAttribute( " My Category " ),
new TypeConverterAttribute( typeof (BooleanConverter)));
這是它在 Blend的效果:
某些TypeConverters 甚至提供了可視工具化的提示,用來設定目前屬性.
下面就是一個不錯的例子,ExpandleTypeConverter:
AddMemberAttributes( typeof (myControl), " MyObjectProperty " ,
new CategoryAttribute( " My Category " ),
new TypeConverterAttribute( typeof (ExpandableObjectConverter)));
這是它在 Blend的效果:
然後點選“new” 按鈕會彈出一個“對象”對話框:
PropertyOrderAttribute (Shared)
Property order 屬性可以讓我們定義已存在的屬性在某分類下的排序位置.
我們通過定義三個屬性來加以說明, 為此我打算将MyIntProperty挪到 advanced 區域外. (在blend 設計時的中繼資料檔案)
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new EditorBrowsableAttribute(EditorBrowsableState.Advanced));
在取消 MyIntProperty隐藏之後, 下面是我們預設的屬性排序:
它是基于字母排序方式的.
首先“MyIntProperty”, 第二“MyObjectProperty” ,然後第三 “My String Property”.
現在我們想讓 MyStringProperty 排在第一的位置.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ),
new PropertyOrderAttribute(PropertyOrder.Early));
這是它在 Blend的效果:
我有點想看一下這個PropertyOrder 類了. 讓我們仔細看一下它的 static 成員.
好的, 現在我想讓MyIntProperty 顯示在最後.
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new CategoryAttribute( " My Category " ),
new PropertyOrderAttribute(PropertyOrder.Late));
我們看到在Blend中該屬性目前是最後一項了:
那如果我們将 MyObjectProperty 與“My String Property” 同時定義在PropertyOrder.Early?
AddMemberAttributes( typeof (myControl), " MyObjectProperty " ,
new CategoryAttribute( " My Category " ),
new TypeConverterAttribute( typeof (ExpandableObjectConverter)),
new PropertyOrderAttribute(PropertyOrder.Early));
它們會首先會 property order 排序,然後是基于字母排序:
因為“MyObjectProperty” 與“My String Property” 都有相同的property order, 它們會在内部通過字母進行排序.
現在,基于練習的原因, 我會讓目前的Property Ordering 相同并在内部對這些屬性進行手工排序.
我們嘗試将“My String Property” 移到“MyObjectProperty”之前.
AddMemberAttributes( typeof (myControl), " MyObjectProperty " ,
new CategoryAttribute( " My Category " ),
new TypeConverterAttribute( typeof (ExpandableObjectConverter)),
new PropertyOrderAttribute(PropertyOrder.CreateAfter(PropertyOrder.Early)));
然後我們會看到MyObjectProperty 排到了“My String property”之後:
注意:PropertyOrder.CreateBefore/After 方法會傳回一個PropertyOrder, 我們可以依據我們的喜好手工進行排序。
( 注:作者在此處又結束了一篇,下面是接下來的内容)
NumberRangeAttribute, NumberIncrementAttribute, NumberFormatAttribute (Blend)
在Expression DLLs中這些屬性很重要, 是以它們被用在了 Blend(當然項目)中.
讓我們看一個我們在什麼屬性上聲明使用了它們: Opacity
首先我們注意在此處有一個比例數值. Opacity 被顯示為百分比格式.
接着我們看到有一個辨別符‘%’跟在該數值後. 現在我們通過滑鼠滾輪加/減目前數值.
如果我們 drag 滑鼠并按 SHIFT 鍵會看到遞增機關被加的更大:
或drag 滑鼠并按CTRL + SHIFT 鍵會看到遞增機關被加的更小:
(通過上面的示範)我們能看出兩件事:
1. 有三種 increments/decrements 方式– Small, Large 和 normal
2. 存在0-100的數值範圍
這兩種情況我們之前都看到了,其就是基于這些屬性實作的.
讓我們看一下這些屬性的構造方法:
NumberRangeAttribute 屬性可以讓我們指定最小 & 最大值的範圍.
NumberIncrementAttribute 屬性可以讓我們指定small, default 和large 遞增類型.
NumberFormatAttribute 屬性可以讓我們指寫顯示内容的格式, 數字精度,顯示比率等.
在我們的blend metadata 檔案中, 為MyIntProperty 添加下列屬性:
将數值範圍限制在1-100之間, 指定increments 為0.5, 1 和 5, 顯示比率為 x10 以及格式為 “X%”.
我們将範圍限制在1-100:
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new NumberRangesAttribute( null , 1 , 100 , null , null ));
分别添加 0.5, 1 和 5 increments 用于small, default 和 large increments:
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new NumberRangesAttribute( null , 1 , 100 , null , null ),
new NumberIncrementsAttribute( 0.5 , 1 , 5 ));
最後添加‘X%’格式用于顯示10的比率.
AddMemberAttributes( typeof (myControl), " MyIntProperty " ,
new NumberRangesAttribute( null , 1 , 100 , null , null ),
new NumberIncrementsAttribute( 0.5 , 1 , 5 ),
new NumberFormatAttribute( " 0'%' " , null , 10 ));
運作這個例子并采用普通 dragging :
然後drag 并按下SHIFT 鍵:
這是MyIntProperty實際初始值:
我們得到了我們想要的: 取值範圍在0-100, 顯示比率, 格式以及increments.
( 注:原文作者在此又結束了一章)
ToolboxBrowsableAttribute (Shared)
我們将幾個控件編譯在一起然後在Visual studio Silverlight 工具欄中添加它們或放在Blend庫中.
如果我們打算隐藏這些控件中的一個時, 我們可以使用ToolboxBrowsableAttribute . 一個不錯的
例子就是ComboBox 是可見的,但ComboBoxItem 被隐藏, 日期可見但DayButton 不可見等等.
在Visual Studio 2008 SP1, Blend 2 SP1這兩個工具中,該屬性僅是設計時屬性.
下面我們建立另一個控件.
public class myOtherControl : Control
{
}
在Blend 裡,我們會在asset gallery中看到一個新的控件:
在Visual studio 裡,我們打開 “Choose Items” 對話框, 找到“SIlverlightControls.dll” 并點選.
現在,我們着手在這些對話框中隐藏myOtherControl。 我們在共享設計時中繼資料項目中添加 ToolboxBrowsableAttribute。
AddTypeAttributes( typeof (myOtherControl),
new ToolboxBrowsableAttribute( false ));
之後編輯并重新開機 blend, 我們看到在Blend 的Asset Gallery中不再出現該控件了:
然後在Visual studio 的“Choose Items” 對話框中也沒再顯示myOtherControl:
Inline Editors (Shared)
Editors 是一個在Visual Studio 屬性視窗和 Blend Data Pane中的定制元素.
下面是一些已知的editors 例子:
我們在custom project 項目中定義我們的editors 以避免Silverlight dlls 的二義性引用(ambiguous references).
Naming convention and the very existence of this project are optional.
我們添加一些必要的設計時引用.
也要確定在 “SilverlightControls.Design.dll” 中對“SilverlightControls.Design.Editors.dll”的引用.
我們着手在Editors項目中添加一個ResourceDictionary,将其命名為“EditorDictionary”.
在EditorResources.xaml 裡添加如下的DataTemplate:
< ResourceDictionary xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml " >
< DataTemplate x:Key = " ColorsComboBox " >
< ComboBox Text = " {Binding StringValue} " IsEditable = " True " >
< ComboBoxItem Background = " Red " > Red </ ComboBoxItem >
< ComboBoxItem Background = " Blue " > Blue </ ComboBoxItem >
< ComboBoxItem Background = " Green " > Green </ ComboBoxItem >
< ComboBoxItem Background = " Yellow " > Yellow </ ComboBoxItem >
< ComboBoxItem Background = " Black " > Black </ ComboBoxItem >
</ ComboBox >
</ DataTemplate >
</ ResourceDictionary >
我們得到了一個包括一些ComboBoxItems的 ComboBox。 唯一重要的就是通過設計工具確定字元值以或數值被綁定到.
下面我們添加了一些靜态類來通路我們的Resource dictionary.
public static class EditorDictionary
{
private static ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent( new Uri( " SilverlightControls.Design.Editos;component/EditorDictionary.xaml " , UriKind.Relative));
public static DataTemplate ColorsComboBox
{
get
{
return dictionary[ " ColorsComboBox " ] as DataTemplate;
}
}
}
這個類要做的就是加載Resource dictionary 并包含一個傳回我們 DateTemplate的靜态屬性.
現在讓我們看一個感興趣的地方, 我們會建立一個顯示DataTemplate的自定義inline editor.
public class CustomInlineEditor : PropertyValueEditor
{
public CustomInlineEditor()
{
this .InlineEditorTemplate = EditorDictionary.ColorsComboBox;
}
}
非常直接. PropertyValueEditor 一個有趣的地方 - 這個(我們用于設定DataTemplate)InlineEditorTemplate取自resource dictionary.
最後必須要說的是MyControl.MyStringProperty 有一個自定底 inline editor.
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ),
new PropertyOrderAttribute(PropertyOrder.Early),
PropertyValueEditor.CreateEditorAttribute( typeof (CustomInlineEditor)));
在Blend裡的效果:
然後,我們選取“Green”.
在UI和XAML之間來回進行驅動的是ComboBox 到 StringValue的綁定.
我想從這個練習中得到的不應該是“rainbow colored ComboBoxes不錯” 或“要将任何元素都做為一個inline editor”.
現在我們看到editor 的樣子以及它是如何工作的, 我們應該熟悉3種類型的屬性 editors.
Inline Property Editor :下面的“單行并有屬性名稱和編輯框” 樣式.
Extended Property Editor:有内部的editor, 但也可根據使用者操作彈出或占用一些區域(顯示).
Dialog property editor – 一個具有觸發對話框按鈕的Inline property editor.
Expended Property Editor
正如上面所見, 一個expended property editor 就是一個inline editor,其基于使用者操作來占用更大的螢幕區域.
讓我們看看如何建立它.
我們着手在EditorResources.xaml檔案中建立另一個 DataTemplate(InlineValueEditorTemplate):
< DataTemplate x:Key = " SimpleTextBox " >
< StackPanel Orientation = " Horizontal " >
< TextBox Text = " {Binding StringValue} " />
< PropertyEditing:EditModeSwitchButton />
</ StackPanel >
</ DataTemplate >
我們看到僅有一個綁定StringValue的TextBox。我們在前面的inline property editor中已看到過.
有趣的是這裡有一個“EditModeSwitchButton”. 它内置一個extensibility 按鈕,它可展開/收縮extended template
視圖.這個按鈕的作用就是連結到 extensibility 的PropertyValueEditorCommands,該指令負責顯示或隐藏extended視圖.
現在我們有了InlineValueEditorTemplate, 下面添加另外一個 DataTemplate(ExtendedValieEditorTemplate):
< DataTemplate x:Key ="HelloWorldListBox" >
< ListBox SelectedItem =" {Binding StringValue} " >
< ListBox.ItemsSource >
< x:Array Type =" {x:Type System:String} " >
< System:String > Hello </ System:String >
< System:String > World </ System:String >
< System:String > foo </ System:String >
< System:String > bar </ System:String >
</ x:Array >
</ ListBox.ItemsSource >
< ListBox.ItemTemplate >
< DataTemplate >
< TextBlock Text =" {Binding} " />
</ DataTemplate >
</ ListBox.ItemTemplate >
</ ListBox >
</ DataTemplate >
盡管這個DataTemplate 看着有點意思, 然而實際上并不是這樣. 它僅是一組在Listbox中作為 TextBlock顯示出來的字元串.
唯一有趣的是它是我們将這個ListBox 的SelectedItem 綁定到 StringValue.
下面, 我們會在EditorDictionary類中建立這兩個強類型(命名)的屬性:
public static DataTemplate SimpleTextBox
{
get
{
return dictionary[ " SimpleTextBox " ] as DataTemplate;
}
}
public static DataTemplate HelloWorldListBox
{
get
{
return dictionary[ " HelloWorldListBox " ] as DataTemplate;
}
}
最後,我們建立一個ExtendedValuePropertyEditor 來使用我們上面的兩個新的 DataTemplates:
public class CustomExtendedEditor : ExtendedPropertyValueEditor
{
public CustomExtendedEditor()
{
this .InlineEditorTemplate = EditorDictionary.SimpleTextBox;
this .ExtendedEditorTemplate = EditorDictionary.HelloWorldListBox;
}
}
還有要把CustomExtendedEditor綁定到我們的 MyStringProperty 上:
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ),
new PropertyOrderAttribute(PropertyOrder.Early),
PropertyValueEditor.CreateEditorAttribute( typeof (CustomExtendedEditor)));
這是在 Blend 中的效果:
點選該按鈕之後 ,我們就得到了這個ExtendedEditorTemplate 彈出框:
我們點選另一個按鈕會看到ExtendedEditorTemplate 在data pane裡占用了更多的空間:
選擇這個清單項中的任一個值都會造成Textbox 和後面屬性的更新:
并再點選按鈕時會關閉該extended editor:
Dialog Property Editor
除了用 Extended template 顯示 inline editor, 我們可能打算用彈出對話框方式來顯示(inline editor).
我們使用已存在的resource dictionary來建立一個對話框 editor 。
public class CustomDialogEditor : DialogPropertyValueEditor
{
public CustomDialogEditor()
{
this .InlineEditorTemplate = EditorDictionary.SimpleTextBox;
this .DialogEditorTemplate = EditorDictionary.HelloWorldListBox;
}
}
我們需要将custom dialog editor 綁定到MyStringProperty上:
AddMemberAttributes( typeof (myControl), " MyStringProperty " ,
new DescriptionAttribute( " I am a property " ),
new DisplayNameAttribute( " My String Property " ),
new CategoryAttribute( " My Category " ),
new PropertyOrderAttribute(PropertyOrder.Early),
PropertyValueEditor.CreateEditorAttribute( typeof (CustomDialogEditor)));
當在 Blend 中運作時:
然後在該對話模中修改值也将會造成Textbox 和後面屬性值的變化:
盡管Dialog property editors 與extended property editors有相似的API, 但卻有一個主要不同– 它支援事務
(transactions).
您看到的“OK” 和“Cancel” 按鈕就是基于觸發CommitTransaction 或 AbortTransaction 命名而定義的.
那麼, 如果我們在選擇“World”之後點選 cancel,該屬性會回複到它的原始值“foo”:
希望您從這個tutorial中學到一些知識, 它包括許多 knocks 和 crannies, 但這應該讓您明确您自己要采用的方式,
-- Justin Angel
Microsoft Silverlight Toolkit Program Manager
譯者: 能看到這裡的都不是凡人,起碼你有時間一路閱讀下來。
好了,今天的内容就到這裡。
原文連結:http://www.cnblogs.com/daizhj/archive/2008/11/27/1342175.html
作者: daizhj, 代震軍
Tags: silverlight,blend,wpf,design-time,run-time,interface,Extensibility,設計時,運作時,擴充
網址: http://daizhj.cnblogs.com/