創建一個簡單用戶控件是開始自定義控件的好方法。本章主要介紹創建一個基本的顏色拾取器。接下來分析如何將這個控件分解成功能更強大的基於模板的控件。
創建基本的顏色拾取器很容易。然而,創建自定義顏色拾取器仍是有價值的聯繫,因爲這不僅演示了構建控件的各種重要概念,而且提供了一個實用的功能。
可爲顏色拾取器創建自定義對話框。但如果希望創建能集成進不同窗口的顏色拾取器,使用自定義控件是更好的選擇。最簡單的自定義控件類型是用戶控件,當設計窗口或頁面時通過用戶控件可以使用相同的方式組裝多個元素。因爲僅通過直接組合現有控件並添加功能並不能實現顏色拾取器,所以用戶控件看起來是更合理的選擇。
典型的顏色拾取器允許用戶通過單擊顏色梯度中的某個位置或分別指定紅、綠和藍三元色成分來選擇顏色。下圖顯示了創建的基本顏色拾取器。該顏色拾取器包含三個Slider控件,這些控件用於調節顏色成分,同時使用Rectangle元素預覽選擇的顏色。
一、定義依賴性屬性
創建顏色拾取器的第一步是爲自定義控件庫項目添加用戶控件。當添加用戶控件後,Visual Studio會創建XAML標記文件和相應的包含初始化代碼即事件處理代碼的自定義類。這與創建新的窗口或也賣弄是相同的——唯一的區別在與頂級容器是UserControl類:
<UserControl x:Class="CustomControls.ColorPickerUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Name="colorPicker"> </UserControl>
最簡單的起點是設計用戶控件對外界公開的公共接口。換句話說,就是設計控件使用者使用的魚顏色拾取器進行交互的屬性、方法和事件。
最基本的細節是Color屬性——畢竟,顏色拾取器不過是用於顯示和選擇顏色的特定工具。爲支持WPF特性,如數據綁定、樣式以及動畫,控件的可寫屬性幾乎都是依賴項屬性。
在前面章節中學習過,創建依賴項屬性的第一步是爲之定義靜態字段,並在屬性名稱的後面加上單詞Property:
public static DependencyProperty ColorProperty;
Color屬性將允許控件使用者通過代碼設置或檢索顏色值。然而,顏色拾取器中的滑動條控件也允許用戶修改當前顏色的一個方面。爲實現這一設計,當滑動條額值發生變化時,需要使用事件處理程序進行響應,並且響應地更新Color屬性。但使用數據綁定關聯滑動條會更加清晰。爲使用數據綁定,需要將每個顏色成分定義爲單獨的依賴項屬性:
public static DependencyProperty RedProperty; public static DependencyProperty GreenProperty; public static DependencyProperty BlueProperty;
儘管Color屬性存儲了System.Windows.Media.Color對象,但Red、Green以及Blue屬性將存儲表示每個顏色成分的單個字節值。
爲屬性定義靜態字段只有第一步。還需要有靜態構造函數,用於在用戶控件中註冊這些依賴性屬性,指定屬性的名稱、數據類型以及擁有屬性的控件類。可通過傳遞具有正確標記設置的FrameworkPropertyMetadata對象,在靜態構造函數中指定選擇的特定屬性特性(如值繼承)。還可指出在什麼地方爲驗證、數據強制以及屬性更改通知關聯回調函數。
在顏色拾取器中,只需要考慮一個因素——當各種屬性變化時需要關聯回調函數進行響應。因爲Red、Green和Blue屬性實際上時Color屬性的不同表示,並且如果一個屬性發生變化,就需要確保其他屬性保持同步。
下面是註冊顏色拾取器的4個依賴性屬性的靜態構造函數的代碼:
static ColorPickerUserControl() { ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPickerUserControl), new FrameworkPropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged))); RedProperty = DependencyProperty.Register("Red", typeof(byte), typeof(ColorPickerUserControl), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged))); GreenProperty = DependencyProperty.Register("Green", typeof(byte), typeof(ColorPickerUserControl), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged))); BlueProperty = DependencyProperty.Register("Blue", typeof(byte), typeof(ColorPickerUserControl), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged))); }
現在已經定義了依賴性屬性,可添加標準的屬性封裝器,使範文它們變得更加容易,並可在XAML中使用它們:
public Color Color { get { return (Color)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } } public byte Red { get { return (byte)GetValue(RedProperty); } set { SetValue(RedProperty, value); } } public byte Green { get{return (byte)GetValue(GreenProperty);} set{SetValue(GreenProperty,value);} } public byte Blue { get { return (byte)GetValue(BlueProperty); } set { SetValue(BlueProperty, value); } }
請記住,屬性封裝器不能包含任何邏輯,因爲可直接使用DependencyObject基類的SetValue()和GetValue()方法設置和檢索屬性。例如,在這個示例中的屬性同步邏輯是使用回調函數實現的,當屬性發生變化時通過屬性封裝器或者直接調用SetValue()方法引發回調函數。