五、 基礎知識:使用Attribute來訂製屬性窗口的顯示
控制顯示的機制和用IDL定義的組件是一樣的,不過是增加了元數據特性。控制顯示使用最普遍的特性是BrowsableAttribute。默認狀態下,屬性窗口顯示對像中定義的所有的公開的、可讀的(即public、有get或者set方法的)屬性,並且把他們放在「雜項(Misc)」類別中。下面是一個簡單的組件例子:
public class SimpleComponent : System.ComponentModel.Component
{
private string data = "(none)";
private bool dataValid = false;
public string Data
{
get
{
return data;
}
set
{
if (value != data)
{
dataValid = true;
data = value;
}
}
}
public bool IsDataValid
{
get
{
// perform some check on the data
//
return dataValid;
}
}
}
下圖是這個例子在屬性窗口中的顯示:
圖1.顯示在屬性窗口中的簡單組件
在這個例子中,SimpleComponent有兩個屬性:Data和IsDataValid。實際上,由於IsDataValid是只讀的,因此顯示在這裡並沒有多大意義,設計人員在設計狀態沒有必要知道這個屬性的值。因此,我們給他加上BrowsableAttribute特性讓屬性窗口不顯示他。
[Browsable(false)]
public bool IsDataValid
{
get
{
// perform some check on the data
//
return dataValid;
}
}
編譯器會自動在特性類名後添加「Attribute」字符,因此我們可以在代碼中省略掉他。當然,輸入「[BrowsableAttribute(false)]」是一樣的效果。對於那些沒有指定特性的屬性或者類,編譯器都使用默認特性和默認特性值加以描述。在這個例子中,BrowsableAttribute的默認值為true。這個原則對於Visual Basic .NET同樣是一致的。兩者唯一的區別就是Visual Basic .NET使用尖括號(『<』和『>』)來標記特性,而不是在C#中使用的中括號(『[』和『]』)。
編譯器會自動在特性類名後添加「Attribute」字符,因此我們可以在代碼中省略掉他。當然,輸入「[BrowsableAttribute(false)]」是一樣的效果。對於那些沒有指定特性的屬性或者類,編譯器都使用默認特性和默認特性值加以描述。在這個例子中,BrowsableAttribute的默認值為true。這個原則對於Visual Basic .NET同樣是一致的。兩者唯一的區別就是Visual Basic .NET使用尖括號(『<』和『>』)來標記特性,而不是在C#中使用的中括號(『[』和『]』)。
同時,我們注意一下在圖1中Data屬性的值「abc」是粗體。這意味著屬性的值不是默認值,而且這個值在設計器為form或者control生成代碼的時候將會保存下來(即會生成一個賦值語句)。而對於屬性的默認值就沒有必要來生成賦值語句,生成代碼意味著增加組件初始化的時間(InitializeComponent方法)和代碼文件的大小。那麼SimpleComponent該如何將默認值通知屬性窗口的呢?要實現這個特性,我們就需要使用DefaultValueAttribute特性對屬性加以描述,也就可以在對象的構建器中為屬性賦值。當屬性窗口顯示屬性值的時候,它就會比較當前值和DefaultValueAttribute指定的默認值,如果兩者不相等的話,就會把值顯示成粗體。在下面的例子裡,Data屬性的值如果不是「(none)」就會被顯示成粗體。
[DefaultValue("(none)")]
public string Data
{
// . . .
}
我們同樣可以給屬性添加更為複雜的判斷邏輯而不只是一些簡單的固有值的比較,這可以通過給組件增加一些特殊方法加以實現。屬性判斷邏輯方法的名字必須以「ShouldSerialize」開頭,並且接著就是屬性的名字,而且此方法的返回值為「Boolean」。在這個例子裡,這種方法就叫「ShouldSerializeData」。在SimpleComponent組件中增加下面的代碼就可以實現和DefaultValueAttribute同樣的效果,不過他卻可以有更強的邏輯代碼。
private bool ShouldSerializeData()
{
return Data != "(none)";
}
一般來說,將屬性分類對設計者來說界面更加友好。我們就是用CategoryAttribute特性來給屬性分類。這個特性就使用一個簡單的類目字符串,屬性窗口可以據此將屬性顯示在類目的子項中。類目名稱可以自行決定。
[DefaultValue("(none)"), Category("Sample")]
public string Data
{
// . . .
}
組件開發者經常遇到的一個問題就是如何實現這個類目字符串的本地化。我們看看CategoryAttribute類,就可以看到他的GetLocalizedString方法就提供了這樣的功能。要實現類目字符串的本地化,就要從CategoryAttribute類派生新的特性類。在這個例子裡,我們從組件的字串資源中得到以鍵值為索引的本地化的類目字符串。在指定屬性的CategoryAttribute特性時,用這個鍵值(Key)替換原來的類目名作為輸入參數。這樣屬性窗在查詢屬性的CategoryAttribute就會調用GetLocalizedString方法並且把key值作為參數傳入方法,在屬性窗口中顯示返回屬性的類目名稱。
internal class LocCategoryAttribute : CategoryAttribute
{
public LocCategoryAttribute(string categoryKey) : base(categoryKey)
{
}
protected override string GetLocalizedString(string key)
{
// get the resource set for the current locale.
//
ResourceManager resourceManager = new ResourceManager();
string categoryName = null;
// walk up the cultures until we find one with
// this key as a resource as our category name
// if we reach the invariant culture, quit.
//
for (CultureInfo culture = CultureInfo.CurrentCulture;
categoryName == null &&
culture != CultureInfo.InvariantCulture;
culture = culture.Parent)
{
categoryName = (string)resourceManager.GetObject(key, culture);
}
return categoryName;
}
}
使用這個本地化類目名稱,我們要先定義一個包含有這個key的資源文件,並且把這個特性使用到屬性上去。
[LocCategory("SampleKey")]
public string Data
{
// . . .
}
當我們在設計窗口的時候,如果選擇了Form上的多個組件,屬性窗口就會顯示他們共同屬性,共同屬性是按組件的屬性名和類型區別的。如果改變共同屬性的值,那麼所有的被選擇的組件的這個屬性值都會跟著改變。而把一個屬性包括在和其他屬性的合成裡一般來說意義不大。通常,共同屬性都是有唯一的值,比如另一個組件的名字。由於當選擇了多個組件的時候,改變共同屬性的值,所有的被選擇組件的對應屬性值都會改變,因此,需要指定某些屬性不被合併為共同屬性。MergablePropertyAttribute就可以做到這點。只要簡單的把這個特性參數設為false值,就可以在選擇多個組件的時候隱藏掉這個屬性,自然他的值就不會跟著變化了。
一些屬性的值可以影響到另外的一些屬性值。例如,在實現數據綁定的組件中,清除掉DataSource的值,當然應該清除掉DataMember的值。RefreshPropertiesAttribute就讓我們實現這個功能。他的默認參數是「none,」也就是不影響到其他屬性,如果指定其他的特性參數,屬性窗口就可以在改變這個屬性值的同時,自動的更新其他屬性的值。另外的兩個值,其一是Repaint,它讓屬性窗口重新獲取屬性的值並且重畫他們;還有就是All,它就意味著組件自己要重新獲取屬性的值。如果值的改變導致屬性的數目的增減,那麼我們就要使用All。不過要注意到這個一般用在高級場合,並且速度要比Repaint慢。RefreshProperties.Repaint適用於大部分的情況。需要注意的是,這個特性是用在引發改變的屬性上,而不是被改編的屬性上。
最後,DefaultPropertyAttribute和DefaultEventAttribute使用在類級別的場合,他們讓屬性窗口高亮顯示這些屬性和事件。當選擇其他的組件的時候,屬性窗口總是試圖定位到和上次選擇的組件相同的屬性或事件上。如果沒有找到這樣的屬性或事件的話,它就會將輸入指針定位到DefaultPropertyAttribute指定的屬性。在Event視圖就會定位到DefaultEventAttribute指定的事件。同時,這個事件也是當你雙擊組件時,自動使用的事件。
-----------------------------------
<<<<<<<<<<<<待續>>>>>>>>>>>>
|
|