Silverlight 用戶控件與自定義控件詳解


Silverlight中你如果想把UI封裝成單獨的一部分或者創建一個新的頁面,你可能會在Visual Studio中通過右擊項目-> 添加-> 添加新項->Silverlight用戶控件這樣來創建控件。如果你是這么做的,那么這篇文章非常適合你。它將適用於任何基於XAML技術:WPFsilverlightWindows Phone Windows 8 Runtime

 

盡管用戶控件很棒,它們能快速的拼在一起,或一次又一次的重復使用,這是它們的很大一個價值所在。但是如果我告訴你還有另一種控件類型,具有干凈的代碼、更強大性能更好,而且比用戶控件的方式更加靈活、重復的使用,那它將會是大量開發人員的最愛嗎?

 

其實這個你早就知道,因為你已經一直在使用他們:ButtonListBoxItemsControlsGridStackPanel等。你可以查看Xaml Style徹底改變控件的外觀和體驗,而不觸及任何代碼。這是多么強大的想法,看看下面一個Silverlight ListBox 行星DEMO 。在左邊,你會看到一個綁定了行星名單的ListBox。在右邊,你能看到一個太陽系,但事實上,這也是一個ListBox。這里沒有涉及到額外的代碼,完全是由修改Template達到效果。你可以按上下鍵,它有正常ListBox的功能。

 

讓我重復一遍:做到這一點我沒有添加任何后台代碼到ListBox。事實上,該頁面后台代碼完全是空的。如果你不相信,這里有源碼下載 

Get Microsoft 
Silverlight

解剖用戶控件

 

首先,讓我們解剖一個典型的用戶控件看看,充分了解下它是怎么工作的這是關鍵。在下面我們控件中一部分XAML確定了布局,為了保持它是一個簡單的例子,里有只一個Grid和一個Button

復制代碼
1  < UserControl  x:Class ="MyApp.SilverlightControl1"
2      xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3      xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" >
4     
5      < Grid  x:Name ="LayoutRoot"  Background ="White" >
6          < Button  Content ="Click Me"  Click ="Button_Click"  Opacity =".5"   />
7      </ Grid >
8  </ UserControl >
復制代碼

 

我們控件的后台代碼:

復制代碼
 1  using System.Windows;
 2  using System.Windows.Controls;
 3  using System.Windows.Media;
 4 
 5  namespace SolarSystemRetemplate
 6 {
 7      public  partial  class SilverlightControl1 : UserControl
 8     {
 9          public SilverlightControl1()
10         {
11             InitializeComponent();
12         }
13 
14          private  void Button_Click( object sender, RoutedEventArgs e)
15         {
16             LayoutRoot.Background =  new SolidColorBrush(Colors.Red);
17         }
18     }
19 }
復制代碼

 

這里有兩個地方值得注意:”LayoutRoot”是在XAML中使用X:Name定義的,我們在后台代碼中通過這個名字自動獲取了這個變量。 而且ButtonClick事件與后台代碼中的事件處理程序奇跡般的掛接了。實際上這是編譯程序和調用方法InitializeComponent處理了這一切--但是有趣的是這個方法在這里不存在。實際上為了表示這是一個局部類,Visual Studio為你私底下創建了一個小(秘密)文件。你可以右擊方法選擇“轉到定義“。下面是該文件的內容:

復制代碼
 1  namespace MyApp {    
 2     
 3      public  partial  class SilverlightControl1 : System.Windows.Controls.UserControl {
 4         
 5          internal System.Windows.Controls.Grid LayoutRoot;
 6         
 7          private  bool _contentLoaded;
 8         
 9          ///   <summary>
10           ///  InitializeComponent
11           ///   </summary>
12          [System.Diagnostics.DebuggerNonUserCodeAttribute()]
13          public  void InitializeComponent() {
14              if (_contentLoaded)
15                  return;
16             _contentLoaded =  true;
17             System.Windows.Application.LoadComponent( this
18                  new System.Uri( " /MyApp;component/SilverlightControl1.xaml ",
19                 System.UriKind.Relative));
20              this.LayoutRoot = ((System.Windows.Controls.Grid)( this.FindName( " LayoutRoot ")));
21         }
22     }

23 }

復制代碼

 

你會注意到LayoutRoot在這里被定義成internal,並且它的賦值使用了“FindName”方法。

 

這就是使用用戶控件的好處之一:它會自動為你做很多工作,但自定義控件則需要你自己來完成這些工作(但是如果考慮到你的效率的話,這並不是那么糟糕)。這里說明下:用戶控件只是另一種自定義控件。

 

解剖自定義控件

自定義控件不像用戶控件會有一個xaml和一個后台代碼組成,換成除了一個默認的XAML Template以外其余的全部是代碼。你可以認為XAML Template和用戶控件的XAML文件作用一樣,但是這里要注意,XAML Template可以實現任何改變。這里要注意另外一件事件,因為Template不具有Visual Studio為您生成的隱藏代碼局部類,所以任何事件處理程序不能在Template中定義。那么我們怎樣重新創建上述用戶控件為一個自定義控件呢?

 

對於Silverlight這是很容易的,右鍵單擊您的項目,選擇 “添加 -> 新建項 –> Silverlight模板化控件”。WPF Windows Phone不伴隨此模板,所以你必須手工通過創建一個類和一個通用模板文件。你做到了這一點后你會發現兩個新文件:首先一個簡單的C#類,第二個是在\Themes\Generic.xaml下創建了一個新文件。第二個文件匯集了你所有控件的Template樣式。它的名字必須是Generic.xaml而且必須在該目錄下,這樣自定義控件才能使用所有的Template

 

下面讓我們一起來看看Template是怎么寫的,和上面用戶控件一樣也是添加了一個Button和一個Grid。

按 Ctrl+C 復制代碼
按 Ctrl+C 復制代碼

 

首先第一,注意BorderTemplateBinding語句,它是控件中一個重要的功能。您可以直接在你的控件代碼中定義一個依賴項屬性綁定。由於自定義控件繼承Control,你將自動繼承Background BorderBrushBorderThickness 和其他屬性。請注意 我這里我沒有給按鈕添加click事件。如果這里添加了,模板將會加載失敗。我們將在后台加上click處理程序,接下來,讓我們一起看代碼吧: 

復制代碼
 1  using System.Windows;
 2  using System.Windows.Controls;
 3  using System.Windows.Controls.Primitives;
 4  using System.Windows.Media;
 5 
 6  namespace MyApp
 7 {
 8     [TemplatePart(Name= " LayoutRoot ", Type= typeof(Control))]
 9     [TemplatePart(Name =  " ClickButton ", Type =  typeof(ButtonBase))]
10      public  class TemplatedControl1 : Control
11     {
12         Control layoutRoot;
13         ButtonBase button;
14          public TemplatedControl1()
15         {
16              this.DefaultStyleKey =  typeof(TemplatedControl1);
17         }
18          public  override  void OnApplyTemplate()
19         {
20              if (button !=  null// unhook from previous template part
21              {
22                 button.Click -=  new RoutedEventHandler(button_Click);
23             }    
24             button = GetTemplateChild( " ClickButton "as ButtonBase;
25              if (button !=  null)
26             {
27                 button.Click +=  new RoutedEventHandler(button_Click);
28             }
29             layoutRoot = GetTemplateChild( " LayoutRoot "as Panel;
30              base.OnApplyTemplate();
31         }
32 
33          private  void button_Click( object sender, RoutedEventArgs e)
34         {
35             layoutRoot.Background =  new SolidColorBrush(Colors.Red);
36         }
37     } 38 } 
復制代碼


 

首先在控件中聲明”TemplatePart”,它指定預期元素的名稱和和類型。在demo LayoutRoot的類型是PanelGrid的類型是Control)、ClickButton的類型是ButtonBase。這些不是嚴格要求,但是當你調用寫好的自定義控件時,它們能幫助Expression Blend了解模板的要求。我總是控件層次結構申明需要的最小類型,使Template更加靈活。比如我用ButtonBase而不是Button,因為我只要用到定義ButtonBase基類的Click事件。同樣LayoutRoot也一樣,我只需要它的BackGround 屬性。 

 

在構造函數中,我定義了”DefaultStyleKey”,它告訴Framework我在Themes\Generic.xaml中定義了默認Template 

 

最后,最重要的部分是”OnApplyTemplate”,此方法當Template加載完后被調用。這是我們早期的機會,搶先對Templatecontrols的引用,即控件中申明的TemplatePart。在這種情況下,我搶先引用在Template中定義ButtonBase,如果找到它,我將給它添加一個click事件處理程序。此外,如果一個新的Template被應用,一定要記住去除以前實例中的事情處理程序。同樣重要要注意的是Template部件總是可選的!所以你要檢查所有引用template的部件是否為null 

 

添加Visual States到控件

 現在添加一些鼠標狀態到我們的控件,並控制動畫何時觸發。在后台代碼中我們定義的添加兩個TemplateVisualState屬性:

1 [TemplateVisualState(GroupName =  " HoverStates ", Name =  " MouseOver ")]
2 [TemplateVisualState(GroupName =  " HoverStates ", Name =  " Normal ")] 

 

接下來給控件添加visual state的觸發:

復制代碼
 1  bool isMouseOver;
 2  protected  override  void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
 3 {
 4     isMouseOver =  true;
 5     ChangeVisualState( true);
 6      base.OnMouseEnter(e);
 7 }
 8  protected  override  void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
 9 {
10     isMouseOver =  false;
11     ChangeVisualState( true);
12      base.OnMouseLeave(e);
13 }
14 
15  private  void ChangeVisualState( bool useTransitions)
16 {
17      if (isMouseOver)
18     {
19         GoToState(useTransitions,  " MouseOver ");
20     }
21      else
22     {
23         GoToState(useTransitions,  " Normal ");
24     }
25 }
26 
27  private  bool GoToState( bool useTransitions,  string stateName)
28 {
29      return VisualStateManager.GoToState( this, stateName, useTransitions);  30 }
復制代碼

 

這正是我們需要的所有代碼。它非常簡單。如果鼠標停留,則觸發MouseOver狀態,否則則觸發正常狀態。請注意,實際上我們沒有真正定義什么是”MouseOver”,這是Template的工作。好接下來讓我們來定義:

復制代碼
 1  < ControlTemplate  TargetType ="local:TemplatedControl1" >
 2      < Border  Background =" {TemplateBinding Background} "
 3              BorderBrush =" {TemplateBinding BorderBrush} "
 4              BorderThickness =" {TemplateBinding BorderThickness} " >
 5          < VisualStateManager.VisualStateGroups >
 6              < VisualStateGroup  x:Name ="HoverStates" >
 7                  < VisualState  x:Name ="MouseOver" >
 8                      < Storyboard >
 9                          < ColorAnimation
10                               Storyboard.TargetName ="BackgroundElement"
11                              Storyboard.TargetProperty ="(Rectangle.Fill).(SolidColorBrush.Color)"
12                              To ="Yellow"  Duration ="0:0:.5"   />
13                      </ Storyboard >
14                  </ VisualState >
15                  < VisualState  x:Name ="Normal" >
16                      < Storyboard >
17                          < ColorAnimation
18                               Storyboard.TargetName ="BackgroundElement"
19                              Storyboard.TargetProperty ="(Rectangle.Fill).(SolidColorBrush.Color)"
20                              To ="Transparent"  Duration ="0:0:.5"   />
21                      </ Storyboard >
22                  </ VisualState >
23              </ VisualStateGroup >
24          </ VisualStateManager.VisualStateGroups >
25          < Grid  x:Name ="LayoutRoot" >
26              < Rectangle  x:Name ="BackgroundElement"  Fill ="Transparent"   />
27              < Button  x:Name ="ClickButton"  
28                      Content ="Click me!"  Opacity =".5"   />
29          </ Grid >
30      </ Border >  31 </ ControlTemplate >
復制代碼

 好了,你現在有一個控件,當ButtonBase被點擊以及鼠標懸停或離開時,Panel的背景色會改變,這樣可以解決於很多控件,不用重寫代碼。

 

 

出處:http://lmyhao.cnblogs.com/


免責聲明!

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



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