WPF 構建無外觀(Lookless)控件


構建一個用戶可以使用Template屬性設置外觀的WPF控件需要以下幾步

  1. 1、繼承自System.Windows.Controls.Control
    2、設置DefaultStyleKeyProperty
    3、實現控件功能
  2. 4、定義默認Sytle
  3. 5、添加ThemeInfo
    我借用一個時鍾的控件例子,講解以下每一個步驟
  4. 第1步 繼承自System.Windows.Controls.Control

 

我們的自定義控件繼承自System.Windows.Controls.Control,如果有更特別的控件,也可以繼承自更復雜的控件。

類聲明

   1: public class Clock : Control
   2: {
   3: }

 

第2步 設置DefaultStyleKeyProperty

 

無外觀的控件需要在靜態構造函數中設置DefaultStyleKeyProperty,這樣它會去在themes/generic.xaml的文件獲取默認的樣式。

   1: static Clock()
   2: {
   3:     DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
   4: }

第3步 實現控件功能

這一步沒什么特別的,就是內部有個DateTime類型和一個DispatcherTimer,完成一個時鍾的功能

   1: public class Clock : Control
   2: {
   3:     private DispatcherTimer timer;
   4:  
   5:     static Clock()
   6:     {
   7:         DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
   8:     }
   9:  
  10:     protected override void OnInitialized(EventArgs e)
  11:     {
  12:         base.OnInitialized(e);
  13:  
  14:         UpdateDateTime();
  15:  
  16:         timer = new DispatcherTimer();
  17:         timer.Interval = TimeSpan.FromMilliseconds(1000 - DateTime.Now.Millisecond);
  18:         timer.Tick += new EventHandler(Timer_Tick);
  19:         timer.Start();
  20:     }
  21:  
  22:     private void Timer_Tick(object sender, EventArgs e)
  23:     {
  24:         UpdateDateTime();
  25:  
  26:         timer.Interval = TimeSpan.FromMilliseconds(1000 - DateTime.Now.Millisecond);
  27:         timer.Start();
  28:     }
  29:  
  30:     private void UpdateDateTime()
  31:     {
  32:         this.DateTime = System.DateTime.Now;
  33:     }
  34:  
  35:     public DateTime DateTime
  36:     {
  37:         get
  38:         {
  39:             return (DateTime)GetValue(DateTimeProperty);
  40:         }
  41:         private set
  42:         {
  43:             SetValue(DateTimeProperty, value);
  44:         }
  45:     }
  46:  
  47:     public static DependencyProperty DateTimeProperty = DependencyProperty.Register(
  48:             "DateTime",
  49:             typeof(DateTime),
  50:             typeof(Clock),
  51:             new PropertyMetadata(DateTime.Now, new PropertyChangedCallback(OnDateTimeInvalidated)));
  52:  
  53:     public static readonly RoutedEvent DateTimeChangedEvent =
  54:         EventManager.RegisterRoutedEvent("DateTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime>), typeof(Clock));
  55:  
  56:     protected virtual void OnDateTimeChanged(DateTime oldValue, DateTime newValue)
  57:     {
  58:         RoutedPropertyChangedEventArgs<DateTime> args = new RoutedPropertyChangedEventArgs<DateTime>(oldValue, newValue);
  59:         args.RoutedEvent = Clock.DateTimeChangedEvent;
  60:         RaiseEvent(args);
  61:     }
  62:  
  63:     private static void OnDateTimeInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
  64:     {
  65:         Clock clock = (Clock)d;
  66:  
  67:         DateTime oldValue = (DateTime)e.OldValue;
  68:         DateTime newValue = (DateTime)e.NewValue;
  69:  
  70:         clock.OnDateTimeChanged(oldValue, newValue);
  71:     }
  72: }

 

第4步 定義默認Sytle

 

如上所述,我們還需要一個默認的Template,它寫在themes/generic.xaml文件中,這個文件可能需要自己創建

   1: <ResourceDictionary
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:local="clr-namespace:CustomControlLibrary"
   5:     >
   6:  
   7:     <Style TargetType="{x:Type local:Clock}">
   8:         <Setter Property="Template">
   9:             <Setter.Value>
  10:                 <ControlTemplate TargetType="{x:Type local:Clock}">
  11:                     <TextBlock Text="{Binding Path=DateTime, RelativeSource={RelativeSource TemplatedParent}}" />
  12:                 </ControlTemplate>
  13:             </Setter.Value>
  14:         </Setter>
  15:     </Style>
  16: </ResourceDictionary>
這里要注意TargetType的使用。到這里控件還是不能找到默認的模板,還需要下面的最后一步

 

  1. 第5步 添加ThemeInfo

 

最后還需要在AssemblyInfo.cs文件中加入ThemeInfo信息,在文件中加入以下特性

   1: [assembly: ThemeInfo(
   2:     ResourceDictionaryLocation.None,
   3:     ResourceDictionaryLocation.SourceAssembly
   4: )]

 

  1. 附注

 

如果需要在模板中使用特定的元素名,來或許模板中的某個特定控件,還有幾個地方需要注意。

1、模板內的Name最好以PART開頭,好的名稱類似於PART_TextBox

2、獲取Template中控件的代碼,需要重載OnApplyTemplate()方法,可以使用GetTemplateChild方法獲取,如下
   1: public override void OnApplyTemplate()
   2: {
   3:     base.OnApplyTemplate();
   4:  
   5:     //從模板中獲取名稱為PART_PresentationTextBox的TextBox
   6:     _presentationTextBox = GetTemplateChild("PART_PresentationTextBox") as TextBox;
   7:     if(_presentationTextBox != null)
   8:     {
   9:         //對_presentationTextBox進行操作
  10:     }
  11: }

3、在控件類添加TemplatePart

   1: [TemplatePart(Name = "PART_PresentationTextBox", Type = typeof(TextBox))]
   2: public class TimeSpanPicker:Control

這個內容可以參考https://gitcafe.com/atskyline/WPFTimeSpanPickerControl

  1. 參考資料

 

http://www.codeproject.com/Articles/14340/Creating-a-look-less-custom-control-in-WPF

http://www.codeproject.com/Articles/35444/Defining-the-Default-Style-for-a-Lookless-Control


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM