八、 提供可互動的屬性視圖
當你在Visual C# .NET中建立一個項目的時候,你可能會注意到屬性視窗的工具列上有一個像閃電的按鈕,按下這個按鈕屬性視窗就會切換到事件檢視,這樣就可以來編輯事件處理了。
屬性視窗的視圖來自“屬性頁面(Property Tabs)”,因此視圖使用的最主要的類是PropertyTab,命名空間是System.Windows.Forms.Design。一個屬性頁面可以和一個特別的組件、設計文檔關聯起來,或者是可以使用的靜態關聯。和組件或文檔關聯起來的屬性頁面在類上用PropertyTabAttribute特性來指定。這個特性指定要建立的Tab的類型,它在屬性視窗上是否顯示由PropertyTabAttribute的PropertyTabScope參數來指定。指定為Component範圍的屬性頁面的可見度由有PropertyTabAttribute特性的組件的可見度來決定。Document範圍的屬性頁面則可以在當前項目的設計中都可見。他的預設值是PropertyTabScope.Component。
舉一個例子來說,看看“FunkyButton”項目。FunkyButton是一個擴充了PropertyTab的UserControl,而且可以讓我們把控制項定為不規則的多邊形。
圖6. FunkyButton
當前選擇的屬性頁面就是屬性視窗從被選擇的控制項的屬性中得到的。屬性頁面因此就允許來操縱顯示內容的不同集合。Events頁就是像屬性一樣以某種方式來處理事件。在這個例子中,屬性頁面就建立了表示控制項頂點的屬性。
.NET framework中的屬性用PropertyDescriptor類來封裝。PropertyDescriptor本身是一個抽象類別,framework中由他派生的類提供了訪問組件的開放屬性的方法。不過,屬性視窗是直接作用在PropertyDescriptor上,而不是直接作用在屬性上。因此,我們就可以寫自己的PropertyDescriptor來做一些特殊的工作。在這個例子裡,我們就有一個屬性工作表示控制項的頂點數,另一個就表示每一個頂點。再次注意一下,我們在屬性視窗上增加頁並不相應的作用在其他對象上。
當屬性視窗向PropertyTab詢問Properties的時候,它就調用GetProperties方法。對於我們的樣本程式,這個方法就像下面的一樣:
public override PropertyDescriptorCollection
GetProperties(ITypeDescriptorContext context, object component,
Attribute[] attrs)
{
// our list of props.
//
ArrayList propList = new ArrayList();
// add the property for our count of vertices
//
propList.Add(new NumPointsPropertyDescriptor(this));
// add a property descriptor for each vertex
//
for (inti = 0; i < ((FunkyButton)component).Points.Count; i++)
{
propList.Add(new VertexPropertyDescriptor(this,i));
}
// return the collection of PropertyDescriptors.
PropertyDescriptor[] props =
(PropertyDescriptor[])propList.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
GetProperties僅僅是返回一些屬性描述的集合。PropertyDescriptors是相當的簡單,仔細查看這些代碼以瞭解他們是怎麼工作的。
FunkyButton同時樣本了下拉式清單編輯器的實現。對於每一個點,我們不是簡單的輸入座標的X和Y值,我們會圖示FunkyButton的形狀,而且可以用圖形化的方法改變點的位置。這樣設定的編輯樣式更加地友好。
圖7. 圖形化的點向量
由於訂製的PropertyTab提供了屬性,重載這個屬性的編輯器也是很容易的。只要簡單地重載PropertyDescriptor的GetEditor方法,然後返回訂製組件的執行個體就可以了。
public override object GetEditor(Type editorBaseType)
{
// make sure we're looking for a UITypeEditor.
//
if (editorBaseType == typeof(System.Drawing.Design.UITypeEditor))
{
// create and return one of our editors.
//
if (editor == null)
{
editor = new PointUIEditor(owner.target);
}
return editor;
}
return base.GetEditor(editorBaseType);
}
設計編輯器同樣簡單。編輯器就是一個簡單的UserControl,所以我們就可以像設計其他的windowsForms對象一樣來做。
圖8. Designing the editor
最後,當使用者在屬性視窗中點擊下拉式箭頭時,我們的編輯器就可以將剛才建立UI編輯器彈出來了。PointUIEditor中的UITypeEditor.EditValue重載後就可以實現了。
public override object EditValue(
ITypeDescriptorContext context,
IServiceProvider sp, object value)
{
// get the editor service.
IWindowsFormsEditorService edSvc =
(IWindowsFormsEditorService)sp.GetService(typeof(IWindowsFormsEditorService));
// create our UI
if (ui == null)
{
ui = new PointEditorControl();
}
// initialize the ui with the settings for this vertex
ui.SelectedPoint = (Point)value;
ui.EditorService = edSvc;
ui.Target = (FunkyButton)context.Instance;
// instruct the editor service to display the control as a
// dropdown.
edSvc.DropDownControl(ui);
// return the updated value;
return ui.SelectedPoint;
}
九、 我們同樣可以使用它
在你自己的應用中可以擁有和IDE屬性窗一樣的特性。把System.Windows.Forms.PropertyGrid的控制項,添加到IDE中的ToolBox中,通過擷取在ToolBox的Component標籤裡的PropertyGrid。
PropertyGrid和其他的控制項工作是一樣的。你可以anchor或者是Dock他,改變它的色彩等。下面的列表列出了PropertyGrid的一些有趣的屬性。
•SelectedObject
PropertyGrid要顯示的對象
•ToolbarVisible
顯示或者隱藏PropertyGrid頂端的Toolbar
•HelpVisible
顯示或者隱藏PropertyGrid底端的協助文本
•PropertySort
設定PropertyGrid的排序類型 (categorized, alphabetical, etc.).
這些屬性都可以在設計時設定。在運行時,可以操作PropertyGrid讓他顯示的你的對象。下面是顯示一個button的例子。在這個例子中,PorpertyGrid的協助和toolbox都被隱藏了。就像上面提到的,你可以設定他自己的屬性。
十、 結論
NET framework和Visual Studio .NET給屬性視窗增加了相當多的功能。由於屬性視窗是RAD的核心,這些特性可以在保持易用性的同時有很多的擴充,也因此在Visual Basic中用的很普遍。就像可以在我們的程式中使用PropertyGrid,我們可以把更多的時間放在如何寫好程式上,從而簡化我們的UI工作。