天天看點

在WPF中自定義控件 CustomControl

    為快速地為你的 應用 定制一個零部件,你需要的是UserControl,這可以參考在WPF中自定義控件 UserControl, 為了讓你打造的控件更标準化,更靈活以及更具有普遍意義,你需要用到的CustomControl,這正是本文要介紹的.

    1、建立CustomControl

  在選擇控件基類後,第一件事情便是在你的項目中建立"CustomControl",我們會發現在項目中自動生成了一個*.CS(或*.VB或其他)檔案以及ThemesGeneric.xaml(如果原來沒有的話),他們分别是CustomControl的背景代碼檔案(Code Behind)與控件的預設主題檔案,打開ThemesGeneric.xaml,你會發現其中自動生成了一個Style,這是你的控件的預設樣式,正如WPF内置控件也有它的預設樣式一樣.這時,我們的工作就被分成了兩個部分,一是在XXX.cs檔案中編輯控件 邏輯 ,而是在Generic.xaml中編寫其UI.

    2、Generic.xaml中的Style是如何與我們的控件聯系在一起的

  打開XXX.cs檔案,你會發現靜态構造方法中,VS自動地幫你覆寫了控件的DefaultStyleKey值:

static CustomControl1() 
  { 
  	DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1))); 
  }
           

        我們知道DefaultStyleKeyProperty是FrameworkElement以及FrameworkContentElement類用來訓示控件的預設樣式鍵值的屬性,該屬性有一個很特别的地方就是我們不能夠用繼承的思想來思考它,比如說Button的預設樣式鍵值是Style1,其子類MyButton的預設樣式鍵值是Style2(或者沒有指定預設樣式),盡管MyButton可以向上轉型成Button類,但我們并不希望其轉型後的預設樣式鍵值為Style1.是以WPF采用了在子類控件的靜态構造方法中重寫DefaultStyleKey元 資料 的方式來指定該子類控件的預設樣式.上面代碼中,我們将new FrameworkPropertyMetadata(typeof(CustomControl1))指定為其新的中繼資料值,這個值代表着,我們将在資源字典中查找一個鍵值為typeof(CustomControl1)的Style來做為控件的預設樣式.而這個樣式剛好被我們定義在了Generic.xaml中:

<Style TargetType=""> 
         
        <Setter Property="Template"> 
            <Setter.Value> 
                <ControlTemplate TargetType=""> 
                    <Border Background="" 
                            BorderBrush="" 
                            BorderThickness=""> 
                    </Border> 
                </ControlTemplate> 
            </Setter.Value> 
        </Setter> 
    </Style>
           

這是大家可能有個疑問,上面XAML中的Style并沒有指定Key值啊,而我們的控件要求的預設樣式Key值為typeof(CustomControl1), 并且資源字典中的元素肯定是要有Key的? 這是Style的基本知識了,在WPF中,為Style指定Key時有兩種方式:一是明确指定Key,而是在沒有明确指定Key的情況下指定TargetType,WPF會自動地将其可Key設定為typeof(TargetType).如果你有在Blend中為控件打造Style的經驗的話,你會注意到建立一個Style時,Blend會提供一個"Apply to ALL"選項,這也是為什麼你打造的Style可以"Apply to all"的奧秘所在.

  3、 "Generic.xaml"這個名稱并非偶然

  通過上面的叙述,你可能會有沖動将Generic.xaml中的Style代碼剪切出來,粘貼到任何一個我們的控件可以找到的地方,然後把Generic.xaml删掉或改成更優雅的名稱,如果你運氣好的話,這是可行的,因為控件會自下而上(Page,App,Theme)去查找其所需要的Style,但此時你已經犯了一個潛在的錯誤:你沒有為控件提供預設的樣式.這裡的預設樣式其實是說"在預設主題中或沒有為該控件找到目前操作 系統 對應的主題時采用的的樣式".這涉及到WPF中Theme的相關話題了,有興趣可以參考msdn相關SDK.

  4、打造你的控件邏輯

  這是必然的,添加屬性,添加事件,方法等等,這些你可以參考在WPF中自定義控件 UserControl ,這裡就不重複叙述了.

  5、打造控件UI

  這裡值得一提的是我非常佩服在VS的XAML海洋裡"裸泳"的兄弟們,不過我更推薦使用Microsoft Expression Blend來完成這項艱巨的任務.另外,如果你發現WPF内置控件在Blend中很好用而我們自己打造的控件卻不是這樣,那麼請注意了,你的控件邏輯可能 設計 得不規範.

  6、控件UI部分與邏輯部分的耦合度

  這是一個容易被忽略但卻非常重要的問題, 我們之是以使用CustomControl而不是UserControl,是因為我們希望自己的控件能向WPF内置控件一樣,其UI能輕易地被其他使用者定制或我們将來所改變.也就是說其視覺樹不能與背景邏輯糾纏在一起,因為其視覺樹中的元素完全可能被你的控件使用者改變.比如,如果你的控件的視覺樹中有一個Button,而你在該Button的Click事件中做了一些控件的邏輯處理,那麼很可能你的控件打造失敗了,因為該Button可能會在使用者重新定義控件Template時被删除.