依賴屬性(Dependency Properties)進階(一)

接着依賴屬性(Dependency Properties)基礎進行更深一步的學習。

1.依賴屬性提供的屬性功能()

1) 元數據重寫

通過定義 PropertyMetadata,類可以定義依賴項屬性的行爲,例如,其默認值和屬性系統回調。 很多依賴項屬性類都已經將默認元數據作爲其註冊過程的一部分而創建。 這包含作爲 WPF API 一部分的依賴項屬性。 通過其類繼承繼承依賴項屬性的類可以重寫原始的元數據,以便可以通過元數據更改的屬性的特徵將與任何特定於子類的要求匹配。

在依賴項屬性上重寫元數據的操作必須在將該屬性提供給屬性系統使用之前進行,也就是說,在對註冊屬性的對象的特定實例進行實例化之前進行。 必須在將其自身提供爲OverrideMetadata  forType 參數的類型的靜態構造內執行對 OverrideMetadata 的調用。 如果在所有者類型的實例存在之後嘗試更改元數據,這將不會引發異常,但會在屬性系統中導致不一致的行爲。 此外,每種類型只可以重寫一次元數據。 以後在同一類型上重寫元數據的嘗試將會引發異常。

下面示例重寫MyButtonContent依賴屬性,將默認值改爲Override

publicclass MyButton : Button
{
	static MyButton()
	{
		Button.ContentProperty.OverrideMetadata(typeof(MyButton), 			newFrameworkPropertyMetadata("Override"));
	}
}

通過元數據重寫,在項目使用該控件時,設計時的Context就是Override了。

2) 屬性值繼承

屬性值繼承是 Windows PresentationFoundation (WPF) 屬性系統的一項功能。 屬性值繼承使元素樹中的子元素可以從父元素那裏獲取特定屬性的值,並繼承該值,就好像它是在最近的父元素中的任意位置設置的一樣。 父元素還可以通過屬性值繼承來獲得其值,因此係統有可能一直遞歸到頁面根元素。 屬性值繼承不是屬性系統的默認行爲;屬性必須用特定的元數據設置來建立,以便使該屬性能夠對子元素啓動屬性值繼承。

如果Dependency屬性在用註冊的時候時指定Inherits爲不可繼承,這樣繼承就會失效。

如下,將MyButton的FontSize屬性置爲不可繼承:

public class MyButton : Button
{
	static MyButton()
	{
		//…
		FrameworkPropertyMetadata fm = newFrameworkPropertyMetadata();
		fm.Inherits = false;
		Button.FontSizeProperty.OverrideMetadata(typeof(MyButton), fm);
	}
}

項目中使用:

<GridGrid.Column="0" Grid.Row="1"TextElement.FontSize="20">
	<Button Width="250"Content="屬性值繼承" Margin="73,28,73,169" />
	<mycontrol:MyButtonWidth="250" Margin="73,68,73,129" />
</Grid>

效果(第一個button繼承了Grid的字體大小,而第二個沒有繼承)


2. 爲依賴屬性添加所有者類型

將類添加爲針對不同類型註冊的依賴性屬性的所有者。 通過執行此操作,WPF XAML讀取器和屬性系統都可以將該類識別爲屬性的其他所有者。 添加所有者時,也可以選擇添加類來提供類型特定的元數據。使用AddOwner方法。

示例(MyButton上添加一個標識文本(SymbolText)依賴屬性,然後爲其添加所有者MyLabel)

MyButton:

public class MyButton : Button
{
	//…
	public string SymbolText
	{
		get { return (string)GetValue(SymbolTextProperty); }
		set { SetValue(SymbolTextProperty, value); }
	}

	public static readonly DependencyProperty SymbolTextProperty =
		DependencyProperty.Register("SymbolText", typeof(string), typeof(MyButton), new PropertyMetadata("★"));
}
MyLabel(這邊支持元數據重寫,修改了默認的標識文本):
public class MyLabel : Label
{
	//…
	public static readonly DependencyProperty SymbolTextProperty = MyButton.SymbolTextProperty.AddOwner(typeof(MyLabel), new PropertyMetadata("△"));
	public string SymbolText
	{
		get { return (string)this.GetValue(SymbolTextProperty); }
		set { this.SetValue(SymbolTextProperty, value); }
	}
}
樣式代碼就不貼了。

引用代碼

<Grid Grid.Column="0" Grid.Row="1" TextElement.FontSize="20">
	<Button Width="250" Content="屬性值繼承" Margin="73,28,73,169" />
	<mycontrol:MyButton Width="250" Margin="73,68,73,129" />
	<mycontrol:MyLabel Width="250" Content="MyLabel" Margin="73,118,73,79"/>
</Grid>
效果

疑問:原用ImageSource做的,但是默認值一直加不上,高手請賜教,代碼如下:

/// <summary>
/// 圖片
/// <para>這是依賴屬性</para>
/// </summary>
public ImageSource Icon
{
	get { return (ImageSource)GetValue(IconProperty); }
	set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
	DependencyProperty.Register("Icon", typeof(ImageSource), typeof(MyButton), new PropertyMetadata(new BitmapImage(new Uri("/MyControls;component/Image/rabbit_32.png", UriKind.RelativeOrAbsolute))));

3. 只讀依賴屬性

在 Windows Presentation Foundation(WPF) 框架中定義的某些依賴項屬性爲只讀。 通常指定只讀依賴項屬性的原因是:這些屬性應該用於狀態確定,但是有多種因素影響該狀態,從用戶界面設計的角度看,僅將屬性設置爲該狀態並不能達到預期的效果。 例如,通過鼠標輸入確認,屬性 IsMouseOver 實際上僅爲表層狀態。 任何通過避開真正的鼠標輸入以編程方式設置此值的嘗試都將是不可預期的並將導致產生不一致的情況。

只讀依賴屬性創建注意點:

  • 註冊屬性時,調用 RegisterReadOnly 方法而不是常規的 Register 方法進行屬性註冊。
  • 當實現 CLR“包裝”屬性時,請確保該包裝也沒有設置的實現,以使公開的公共包裝的只讀狀態中不存在不一致現象。
  • 只讀註冊返回的對象是 DependencyPropertyKey,而不是 DependencyProperty 您仍應將此字段作爲成員存儲,但是通常不將其設置爲此類型的公共成員。

示例:爲MyTextBox添加只讀依賴屬性,內容爲文本長度:

public class MyTextBox:TextBox
{		
	//只公佈Get方法
	/// <summary>
	/// 文本長度
	/// </summary>
	public int TextCount
	{
		get { return (int)GetValue(TextCountProperty.DependencyProperty); }
		private set { SetValue(TextCountProperty, value); }
	}

	public static readonly DependencyPropertyKey TextCountProperty =
		DependencyProperty.RegisterReadOnly("TextCount", typeof(int), typeof(MyTextBox), new PropertyMetadata(0));
}


4. 依賴屬性元數據

依賴項屬性的元數據還可以由定義依賴項屬性的類來唯一地指定,可以在依賴項屬性添加到另一個類時進行更改,可以由所有從定義基類繼承依賴項屬性的派生類來明確地重寫。

依賴項屬性的元數據對象可以包含以下類型的信息:

  • 依賴項屬性的默認值。
  • 對影響每個所有者類型的強制行爲或更改通知行爲的回調實現的引用。 
  • 如果所討論的依賴項屬性被視爲一個 WPF框架級別的屬性,則元數據中可能包含 WPF框架級別的依賴項屬性特徵,這些特徵報告各種服務(如 WPF框架級別的佈局引擎和屬性繼承邏輯)的信息和狀態。
public static DependencyProperty Register(
	string name,--依賴項對象的名稱
	Type propertyType,--屬性的類型
	Type ownerType,--依賴項對象的所有者類型
	PropertyMetadata typeMetadata,--依賴項對象的屬性元數據
	ValidateValueCallback validateValueCallback--對回調的引用
)

public PropertyMetadata(
	Object defaultValue,--依賴項對象的默認值
	--每當屬性的有效值更改時,屬性系統都將調用該處理程序實現
	PropertyChangedCallback propertyChangedCallback,
	--每當屬性系統對該屬性調用 CoerceValue 時都將調用此處理程序實現
	CoerceValueCallback coerceValueCallback
)
--核心級別具有呈現/用戶界面影響的非框架屬性提供屬性元數據
public UIPropertyMetadata(
	Object defaultValue,
	PropertyChangedCallback propertyChangedCallback,
	CoerceValueCallback coerceValueCallback,
	bool isAnimationProhibited--是否禁止動畫處理,默認爲false
)
--框架的屬性系統
public FrameworkPropertyMetadata(
	Object defaultValue,
	--元數據選項標誌(FrameworkPropertyMetadataOptions 值的組合)
	FrameworkPropertyMetadataOptions flags,
	PropertyChangedCallback propertyChangedCallback,
	CoerceValueCallback coerceValueCallback,
	bool isAnimationProhibited,
	--此屬性的綁定時使用的 UpdateSourceTrigger
	UpdateSourceTrigger defaultUpdateSourceTrigger
)

5依賴屬性回調、驗證及強制值

執行順序:

驗證:ValidateValueCallback

強制值:CoerceValueCallback

回調:PropertyChangedCallback

示例(僅能輸入數字的TextBox,並且有最大最小值驗證)

/// <summary>
/// 文本值,僅能輸入整數
/// </summary>
public string Value
{
	get { return (string)GetValue(ValueProperty); }
	set { SetValue(ValueProperty, value); }
}

public static readonly DependencyProperty ValueProperty =
	DependencyProperty.Register("Value",
		typeof(string),
		typeof(MyTextBox),
		new FrameworkPropertyMetadata(
			"5", 
			FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
			PropertyChanged,
			CoerceValue,
			true,
			UpdateSourceTrigger.LostFocus),
		ValidateValue);
疑問

原繼承TextBox來做的,想替換調原來的Text屬性,沒成功。TextBox中使用PART_ContentHost來指定輸入,並且指定[ContentProperty("Text")],沒找到合適的方法去替換,有高手知道的賜教下。

private static bool ValidateValue(object value)
{
	try
	{
		int d = System.Convert.ToInt32(value);
		Debug.WriteLine("ValidateValue:Pass Value:" + value.ToString());
		return true;
	}
	catch
	{
		Debug.WriteLine("ValidateValue:Fail Value:" + value.ToString());
		return false;
	}
}

private static object CoerceValue(DependencyObject d, object baseValue)
{
	try
	{
		MyTextBox box = (MyTextBox)d;
		int i = Convert.ToInt32(baseValue);
		if (i > box.MaxValue)
		{
			i = box.MaxValue;
		}
		if (i < box.MinValue)
		{
			i = box.MinValue;
		}
		Debug.WriteLine("CoerceValue:Pass Value:" + baseValue.ToString() + " CoerceValue:"+i.ToString());
		((MyTextBox)d).IsLocked = true;
		((MyTextBox)d).Text = i.ToString();
		((MyTextBox)d).IsLocked = false;
		return i.ToString();
	}
	catch
	{
		Debug.WriteLine("CoerceValue:Fail Value:" + baseValue.ToString());
		return ValueProperty.DefaultMetadata.DefaultValue;
	}
}

private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
	//d.CoerceValue(MaxValueProperty);
	Debug.WriteLine("PropertyChanged:Pass Value:" + e.OldValue.ToString() + "->" + e.NewValue.ToString());
}

debug結果:

ValidateValue:PassValue:01

CoerceValue:PassValue:01 CoerceValue:1

ValidateValue:PassValue:1(此次調用應該由於強制回調引起)

PropertyChanged:PassValue:0->1


6.監聽依賴屬性

監聽有兩種方法:
a. 依賴屬性回調,第5點中說明了。
b. 用DependencyPropertyDescriptor類:
使用方法如下(將Text和Value屬性關聯起來):

public MyTextBox()
{
	DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(MyTextBox.TextProperty, typeof(MyTextBox));
	descriptor.AddValueChanged(this, OldTextChanged);
}

private void OldTextChanged(object sender, EventArgs e)
{
	if (IsLocked == false)
	{
		Value = Text;
	}
}



作者:FoolRabbit
出處:http://blog.csdn.net/rabbitsoft_1987
歡迎任何形式的轉載,未經作者同意,請保留此段聲明!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章