2009年4月1日 星期三

[WPF] 相依屬性(Dependency Property) 初探

在傳統的 Window Form 中的設計中,每個控制項都各自擁有自己的 property,而這些 property 大多都是相同的(例如:FontSize),因此不僅浪費記憶體來儲存這些 property 的值,有時在程式中還需要撰寫多餘程式來設定這些 property 的值。

因此,在 WPF 中提出了Dependency Property,用來解決各控制項中重複的 property 過多因此造成浪費記憶體的現象。

以下用範例程式來說明:(節錄自 Application = Code + Markup 一書)

SpaceButton.cs

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SpaceButton : Button
{
//傳統 .NET 做法:私有欄位搭配公開 property
string _txt;

//透過 get & set 設定 property 的處理方式
public string Text
{
set
{
_txt = value;
this.Content = SpaceOutText(_txt);
}
get { return _txt; }
}

//Dependecy Property 的宣告
public static readonly DependencyProperty SpaceProperty;
public int Space
{
//不同與以往 property 的設定方式
//Dependency Property 必須透過 DependencyObject 中的 SetValue() 與 GetValue() 進行 value 的處理
//*** SetValue() 與 GetValue() 皆為 static method ***
set { SetValue(SpaceProperty, value); }
get { return (int)GetValue(SpaceProperty); }
}

//處理 Dependency Property 必須使用 static constructor
static SpaceButton()
{
//設定 metadata (可以想像成是 Dependency Property 的相關特性)
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 1;
meta.AffectsMeasure = true;
meta.Inherits = true;
meta.PropertyChangedCallback += OnSpacePropertyChanged;

//註冊 DependecyProperty
SpaceProperty = DependencyProperty.Register("Space", typeof(int), typeof(SpaceButton), meta, ValidateSpaceValue);
}

//value 檢驗時呼叫方法
static bool ValidateSpaceValue(object obj)
{
int i = (int)obj;
return i >= 0;
}


//當 property 改變時,呼叫此方法
static void OnSpacePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
SpaceButton btn = obj as SpaceButton;
btn.Content = btn.SpaceOutText(btn._txt);
}

//在 Button Content 中每個字元間塞指定數量(Space)的空白
string SpaceOutText(string str)
{
if (str == null)
return null;

StringBuilder build = new StringBuilder();
foreach (char ch in str)
build.Append(ch + new string(' ', Space));

return build.ToString();
}
}
}



SpaceWindow.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SpaceWindow : Window
{
//設定 Dependency Property
public static readonly DependencyProperty SpaceProperty;

public int Space
{
//同樣的,這邊也要透過 DependencyObject 中的 SetValue() 與 GetValue() 進行 value 的處理
set { SetValue(SpaceProperty, value); }
get { return (int)GetValue(SpaceProperty); }
}

//處理 Dependency Property 必須使用 static constructor
static SpaceWindow()
{
//設定 metadata
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.Inherits = true;

//由於 SpaceProperty 在 SpaceButton 中已經宣告過
//因此這邊直接將 SpaceProperty 指到 SpaceButton 中的 SpaceProperty
//只要透過 AddOwner() 將自己也設定為該 Dependency Property 的擁有者即可
SpaceProperty = SpaceButton.SpaceProperty.AddOwner(typeof(SpaceWindow));

//唯一需要注意的是 metadata 並不會一同被採用
//因此需要另外的宣告與設定,並 override
SpaceProperty.OverrideMetadata(typeof(SpaceWindow), meta);
}
}
}



SetSpaceProperty.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SetSpaceProperty : SpaceWindow
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new SetSpaceProperty());
}

public SetSpaceProperty()
{
this.Title = "Set Space Property";
this.SizeToContent = SizeToContent.WidthAndHeight;
this.ResizeMode = ResizeMode.CanMinimize;

int[] iSpaces = { 0, 1, 2 };
Grid grid = new Grid();
this.Content = grid;

//設定 Grid 的 RowDefinition
for (int i = 0; i < 2; i++)
{
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
grid.RowDefinitions.Add(row);
}

//設定 Grid 的 ColumnDefinition
for (int i = 0; i < iSpaces.Length; i++)
{
ColumnDefinition col = new ColumnDefinition();
col.Width = GridLength.Auto;
grid.ColumnDefinitions.Add(col);
}

for (int i = 0; i < iSpaces.Length; i++)
{
SpaceButton btn = new SpaceButton();
btn.Text = "Set window Space to " + iSpaces[i];
btn.Tag = iSpaces[i];
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.VerticalAlignment = VerticalAlignment.Center;
btn.Click += WindowPropertyOnClick;
grid.Children.Add(btn);
Grid.SetRow(btn, 0);
Grid.SetColumn(btn, i);

btn = new SpaceButton();
btn.Text = "Set button Space to " + iSpaces[i];
btn.Tag = iSpaces[i];
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.VerticalAlignment = VerticalAlignment.Center;
btn.Click += ButtonPropertyOnClick;
grid.Children.Add(btn);
Grid.SetRow(btn, 1);
Grid.SetColumn(btn, i);
}
}

//更改 Window 中的 Dependency Property
void WindowPropertyOnClick(object sender, RoutedEventArgs args)
{
SpaceButton btn = args.Source as SpaceButton;

//修改 Window 的 Space Dependency Property
//而此修改會影響所有在 SpaceWindow 中且擁有 SpaceProperty 屬性的控制項
//唯獨已經設定 Space 值的 SpaceButton 控制項例外
Space = (int)btn.Tag;
}

//更改 Button 中的 Dependency Property
void ButtonPropertyOnClick(object sender, RoutedEventArgs args)
{
SpaceButton btn = args.Source as SpaceButton;

//僅單獨修改 SpaceButton 中的 Space 值
//不會影響到 SpaceWindow 中其他擁有 SpaceProperty 的控制項
btn.Space = (int)btn.Tag;
}
}
}



參考資料

1 則留言:

  1. In case you are interested in making money from your visitors by popup advertisments - you should try one of the most reputable networks - Clickadu.

    回覆刪除