WPF——樣式和行爲

  若是侷限於簡單的、灰色外觀的普通按鈕以及其餘經常使用控件,WPF將是沒有新意的捆綁。WPF提供了幾個特性,容許爲基本元素插入一些本身的愛好,並標準化應用程序的可視化外觀。本次主要學習樣式和行爲。樣式是組織和重用格式化選項的重要工具。不是使用重複的標記填充XAML,以設置注入外邊距、內邊距、顏色以及字體等細節,而能夠建立一系列封裝全部這些細節的;樣式。而後能夠在須要之處經過一個屬性應用樣式。行爲是一個重用用戶界面代碼的更有挑戰性的工具。其基本思想是行爲封裝了一些通用的UI功能。若是具備適當的行爲,可使用一兩行XAML標記將其附加到一個元素,從而能夠爲您節省便攜盒調試代碼的工做。canvas

11.1 樣式基礎app

  樣式是能夠應用於元素的屬性值的集合。WPF樣式系統和HTML標記中的層疊樣式表標準扮演相似的角色。與CSS相似,經過WPF樣式能夠定義通用的格式化特性集合,而且爲了保證一致性,在整個應用程序中應用它們。與CSS同樣,WPF樣式也可以自動工做,指定具體的元素類型的目標,而且經過元素樹層疊起來。然而,WPF樣式的功能更增強大,由於它們可以設置任何依賴項屬性。這意味着可使用它們標準化未格式化的特性,如控件的行爲。WPF樣式還支持觸發器,當一個屬性發生變化時能夠經過觸發器改變控件的樣式,而且可使用模板從新定義控件的內置外觀。爲了理解適合使用樣式的場合,分析一個簡單的示例。設想須要標準化在窗口中使用的字體。最簡單的方法是設置包含窗口的字體屬性。這些屬性是在Control類中定義的,得益於這些屬性值的繼承特性,當在窗口級別上設置這些屬性時,在窗口中的全部元素都會使用相同的屬性值,除非明確的覆蓋它們。如今考慮一種狀況,但願只爲用戶界面中的一部分鎖定字體。若是能在一個特定容器中隔離這些元素,可使用本質上相同的方法,並設置容器的字體屬性。若是,咱們但願使全部的按鈕具備抑制的字體和文本尺寸,而且使用和其餘元素不一樣的字體設置。就須要一種方法在某個地方定義這些細節,並在全部應用它們的地方重用這些細節。以下所示:框架

  <Window.Resources>    
    <FontFamily x:Key="ButtonFontFamily">Times New Roman</FontFamily>
    <s:Double x:Key="ButtonFontSize">18</s:Double>
    <FontWeight x:Key="ButtonFontWeight">Bold</FontWeight>    
  </Window.Resources>

這是在窗口中定義的資源,一旦定義了資源,下一步是在元素中實際使用這些資源。ide

    <Button Padding="5" Margin="5"
            FontFamily="{StaticResource ButtonFontFamily}"
            FontWeight="{StaticResource ButtonFontWeight}"
            FontSize="{StaticResource ButtonFontSize}" 
              >A Customized Button</Button>

這種方式雖然將字體細節移除了標記,但還存有問題,一、除惡資源名稱類似以外,沒有明確指明三個資源是相關的。若是須要設置更多的字體屬性,或者決定爲不一樣類型的元素維護不一樣字體設置,這將變的複雜。二、須要使用資源的標記很是繁瑣。咱們能夠經過定義一個將全部字體捆綁在一塊兒的自定義類。樣式對這一問題提供了很是好的解決方案。能夠定義一個獨立的包裝全部但願設置的屬性的樣式。以下:工具

  <Window.Resources>
    <Style x:Key="BigFontButtonStyle">
      <Setter Property="Control.FontFamily" Value="Times New Roman" />
      <Setter Property="Control.FontSize" Value="18" />
      <Setter Property="Control.FontWeight" Value="Bold" />
    </Style>    
  </Window.Resources>

上面的標記建立了一個獨立的資源:一個System.Windows.Style對象。這個樣式對象包含了一個Setter集合,該集合具備三個Setter對象,每一個Setter對象用於一個但願設置的屬性。每一個Setter對象由兩部分信息組成:但願進行設置的屬性和但願爲該屬性應用的值。與全部資源同樣,樣式對象有一個鍵名,從而當須要時能夠從集合中提取它。每一個WPF元素均可以使用一個樣式,樣式經過元素的Style屬性插入到元素中。以下,將上面建立的樣式配置到一個按鈕:學習

    <Button Padding="5" Margin="5"
            Style="{StaticResource BigFontButtonStyle}" 
              >A Customized Button</Button>

也能夠經過代碼設置樣式。須要作的所有工做就是使用熟悉的FindResource()方法,從最近的資源集合中提取樣式。以下:字體

cmdButton.Style=(Style)cmd.FindResource("樣式鍵名");

Setter集合是Style類中最重要的屬性,但並非惟一的屬性,在Style類中共有5個重要屬性,以下表中介紹了這些屬性動畫

Setters 設置屬性值以及自動關聯事件處理程序的Setter對象或EventSetter對象的集合
Triggers 繼承自TriggerBase類而且可以自動改變樣式設置的對象的集合。例如,當另外一個屬性改變時,或者當發生某個事件時,能夠修改樣式
Resources 但願用於樣式的資源集合。例如,可能須要使用一個對象設置多個屬性。這時,做爲資源建立對象,而後再在Setter對象中使用該資源,這樣會更高效
BasedOn 經過該屬性能夠建立繼承自其餘樣式設置的更復雜樣式
TargetType 該屬性表示應用樣式的元素的類型。經過該屬性能夠建立隻影響特定類型元素的設置器,而且還能夠建立可以爲恰當的元素類型自動起做用的設置器

11.1.1 建立樣式對象this

  若是但願建立具備更精細目標的樣式,可使用容器的Resoures集合定義樣式,如StakPanel面板或Grid面板。若是但願在應用程序中重用樣式,可使用應用程序的Resources集合定義樣式。嚴格來說,不須要同時使用樣式和資源。由於沒法與其餘元素共享該樣式,若是隻是使用樣式設置一些屬性,直接設置熟悉更加容易。可是,可使用該方法爲一個元素關聯觸發器。經過該方法還能夠修改元素控件模板的一部分。spa

11.1.2 設置屬性

  每一個Style對象包裝了一個Setter對象的集合。每一個Setter對象設置元素的單個屬性。惟一的限制是設置器只能改變依賴項屬性——不能修改其餘屬性。在某些狀況下,不能使用簡單的特性字符創設置屬性值。例如,不能使用簡單的字符串建立ImageBrush對象。對於這種狀況,可使用屬性的XAML技巧,使用嵌套的元素代替特性。下面是一個示例:

<Style x:key="HappyTiledElementStyle">
    <Setter Property="Control.Background">
        <Setter.Value>
            <ImageBrush TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 32 32" ImageSoure="happyface.jpg" Opacity="0.3">
            </ImageBrush>
        </Setter.Value>
    </Setter>
</Style>

爲了標識但願設置的屬性,須要提供類和屬性的名稱。然而,使用的類名沒必要是定義屬性的類名。也能夠是繼承了屬性的派生類。例如以下樣式,該樣式使用Button類的引用代替了Control類的引用

    <Style x:Key="BigFontButtonStyle">
      <Setter Property="Control.FontFamily" Value="Times New Roman" />
      <Setter Property="Control.FontSize" Value="18" />
      <Setter Property="Control.FontWeight" Value="Bold" />
    </Style>
View Code

 在WPF中還存在這樣一些狀況,在元素框架層次中的多個位置定義了同一個屬性。例如,在Control類和TextBlock類中都定義了所有的字體屬性。若是正在建立應用到TextBlock對象以及繼承自Control類的元素的樣式,能夠按以下方式建立標記。

    <Style x:Key="BigFontButtonStyle">
      <Setter Property="Button.FontFamily" Value="Times New Roman" />
      <Setter Property="Button.FontSize" Value="18" />
      <Setter Property="TextBlock.FontFamily" Value="Arial" />
      <Setter Property="TextBlock.FontSize" Value="10" />
    </Style>
View Code

可是儘管Button.FontFamily屬性和TextBlock.FontFamily屬性是在他們各自的基類中分別聲明,但它們都引用同一個依賴項屬性。因此,當使用這個樣式時,WPF設置FontFamily和FontSize屬性兩次。最後應用的設置具備優先權,而且被同時應用到Button對象和TextBlock對象。儘管這個問題很特別,許多屬性並不存在該問題,但若是常常建立爲不一樣的元素類型應用不一樣格式的樣式,分析是否存在這一問題就顯得很重要了。咱們可使用Style對象的TargetType屬性,指定準備應用屬性的類。以下

    <Style x:Key="BigFontButtonStyle" TargetType="Button">
      <Setter Property="FontFamily" Value="Times New Roman" />
      <Setter Property="FontSize" Value="18" />
      <Setter Property="FontWeight" Value="Bold" />
    </Style>

11.1.3 關聯事件處理程序

  屬性設置器是全部樣式中最多見的要素,可是也能夠建立爲事件關聯特定事件處理程序的EventSetter對象的集合。下面是一個示例,爲MouseEnter和MouseLeave事件關聯事件處理程序:

    <Style x:Key="MouseOverHighlightStyle">
      <Setter Property="TextBlock.Padding" Value="5"/>
      <EventSetter Event="FrameworkElement.MouseEnter" Handler="element_MouseEnter" />
      <EventSetter Event="FrameworkElement.MouseLeave" Handler="element_MouseLeave" />
    </Style>

下面是事件處理代碼

        private void element_MouseEnter(object sender, MouseEventArgs e)
        {
            ((TextBlock)sender).Background = new SolidColorBrush(Colors.LightGoldenrodYellow);
        }
        private void element_MouseLeave(object sender, MouseEventArgs e)
        {
            ((TextBlock)sender).Background = null;
        }

MouseEnter和MouseLeave事件使用直接事件路由,這意味着它們不在元素樹中冒泡和隧道。若是但願爲大量元素應用鼠標懸停其上的效果,須要爲每一個元素添加MouseEnter和MouseLeave事件處理程序。基於樣式的事件處理程序簡化了這一任務。如今只須要應用單個樣式,該樣式包含屬性設置器和事件設置器:

<TextBlock Style="{StaticResource MouseOverHighlightStyle}">Hover over me.</TextBlock>

在WPF中,事件設置器是一種不多使用的技術。若是須要使用此處演示的功能,可能更喜歡使用事件觸發器,它以聲明的方式定義了所但願的行爲。事件觸發器是專門爲實現動畫而設計的。

11.1.4 多層樣式

  儘管能夠砸許多不一樣層次蒂尼任意數量的樣式,可是每一個WPF元素一次只能使用一個樣式對象。乍一看,好像是一種限制,但因爲屬性值繼承和樣式繼承特性,所以實際上這種限制並不存在。例如,設想但願爲一組控件使用相同的字體,而又不想爲每一個控件應用相同的樣式。對於這種狀況能夠經過樣式設置BasedOn特性,使用此類樣式繼承。以下

  <Window.Resources>
    <Style x:Key="BigFontButtonStyle">
      <Setter Property="Control.FontFamily" Value="Times New Roman" />
      <Setter Property="Control.FontSize" Value="18" />
      <Setter Property="Control.FontWeight" Value="Bold" />
    </Style>

    <Style x:Key="EmphasizedBigFontButtonStyle" BasedOn="{StaticResource BigFontButtonStyle}">
      <Setter Property="Control.Foreground" Value="White" />
      <Setter Property="Control.Background" Value="DarkBlue" />
    </Style>
  </Window.Resources>
View Code

11.1.5 經過類型自動應用樣式

  到目前爲止,已經看到了如何建立具備名稱的樣式以及如何在標記中引用它們。但還有另外一種方法,既能夠爲特定類型的元素自動應用樣式。只須要設置TargetType屬性以指定合適的類型,並徹底忽略鍵名。當這樣作時,WPF其實是隱式的使用類型標記擴展設置鍵名,以下所示:

x:Key="{x:Type Button}"

如今樣式被自動應用於整個元素樹中的全部按鈕上。下面是一個示例,能夠獲得與上面相同的結果

<Window.Resources>
    <Style TargetType="Button">
      <Setter Property="FontFamily" Value="Times New Roman" />
      <Setter Property="FontSize" Value="18" />
      <Setter Property="FontWeight" Value="Bold" />
    </Style>
  </Window.Resources>

  <StackPanel Margin="5">
    <Button Padding="5" Margin="5">Customized Button</Button>
    <TextBlock Margin="5">Normal Content.</TextBlock>
    <Button Padding="5" Margin="5" Style="{x:Null}"
            >A Normal Button</Button>
    <TextBlock Margin="5">More normal Content.</TextBlock>
    <Button Padding="5" Margin="5">Another Customized Button</Button>
  </StackPanel>
View Code

不過中間的按鈕經過將樣式設置爲Null有效的刪除了樣式。

儘管自動樣式很是方便,可是它們會讓設計變得複雜。下面是幾條緣由:

  • 在具備許多樣式和多層樣式的複雜窗口中,跟蹤是否經過屬性值繼承或經過樣式設置了某個特定屬性有些困難。所以,若是但願改變一個簡單的細節,須要查看整個窗口的所有標記。
  • 窗口中的格式化操做在開始時一般更通常,而且會逐漸變得愈來愈詳細。若是剛開始爲窗口應用了自動樣式,在許多地方可能須要使用顯示的樣式覆蓋自動樣式。這會使整個設計變得複雜。

11.2 觸發器

  WPF中的一個主題是以聲明方式擴展代碼的功能。當使用樣式、資源以及數據綁定時,將會發現即便不使用代碼,也能完成很多工做。使用觸發器,能夠自動完成簡單的樣式改變,而這一般須要使用樣板事件處理邏輯。例如,當一個屬性發生變化時能夠進行響應,並自動調整樣式。觸發器經過Style.Triggers集合鏈接到樣式。每一個樣式均可以有任意多個觸發器,而且每一個觸發器都是System.Windows.TriggerBase的派生類的實例。下表列出了WPF中的選項

Trigger 這是一種最簡單的觸發器。它監測依賴屬性的變化,而後使用設置器改變樣式
MultiTrigger 與Trigger相似,可是這種觸發器聯合了多個條件。只有知足了全部這些條件,纔會啓動觸發器
DataTrigger 這種觸發器使用數據綁定。它與Trigger相似,只不過它監視的是全部綁定數據的變化
MultiDataTrigger 聯合多個數據觸發器
EventTrigger 這是最複雜的觸發器。當一個事件發生時,這種觸發器應用一個動畫

經過FrameworkElement.Trigger集合,能夠直接爲元素應用觸發器,而不須要建立樣式。但這存在一個至關大的缺陷。這個Triggers集合只支持事件觸發器。

11.2.1 簡單觸發器

  能夠爲任何依賴項屬性關聯一個簡單觸發器。例如,能夠經過響應Control類的IsFocused、IsMouseOver以及IsPressed屬性的變化,建立鼠標懸停效果和焦點效果。每一個簡單觸發器都指定了正在監視的屬性,以及正在等待的屬性值。當該屬性值出現時,將自動應用存儲在Trigger.Setters集合中的設置器。但不能使用複雜的觸發器邏輯。下面的觸發器等待按鈕獲取鍵盤焦點,當獲取焦點時或將其前景色設置爲深紅色。

      <Style.Triggers>
        <Trigger Property="Control.IsFocused" Value="True">
          <Setter Property="Control.Foreground" Value="DarkRed" />
        </Trigger>
        <Trigger Property="Control.IsMouseOver" Value="True">
          <Setter Property="Control.Foreground" Value="LightYellow" />
          <Setter Property="Control.FontWeight" Value="Bold" />
        </Trigger>        
        <Trigger Property="Button.IsPressed" Value="True">
          <Setter Property="Control.Foreground" Value="Red" />
        </Trigger>
      </Style.Triggers>
View Code

能夠建立一次應用於相同元素的多個觸發器。若是這些觸發器設置不一樣的屬性,這種狀況就不會出現混亂。然而,若是多個觸發器修改了相同的屬性,那麼最終是最後的觸發器有效。如上面的示例中IsPressed屬性最後觸發因此它得以實現。觸發器在標記中的排列順序徹底決定了最終的結果。若是但願建立只有當幾個條件都爲真時才激活的觸發器,可使用MultiTrigger。這種觸發器提供了一個Conditions集合,能夠經過該集合定義一系列屬性和值的組合。以下示例中,只有按鈕具備焦點並且鼠標懸停在該按鈕上時,纔會應用格式化信息:

<Style.Triggers>
<MultiTrigger>
    <MultiTrigger.Conditions>
        <Condition Property="Control.IsFocused" Value="True"/>
        <Condition Property="Control.IsMouseOver" Value="True"/>
    </MultiTrigger.Conditions>
    <MultiTrigger.Setters>
        <Setters Property="Control.Foreground" Value="DarkRed"/>
    </MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
View Code

11.2.2 事件觸發器

  普通的觸發器等待一個屬性發生變化,而事件觸發器等待特定的事件被激發。事件觸發器要求用戶提供一系列修改控件的動做。這些動做一般被用於一個動畫。下面使用一個動畫效果是按鈕的FontSize屬性從而造成動畫效果。

        <EventTrigger RoutedEvent="Mouse.MouseEnter">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:0.2"
                  Storyboard.TargetProperty="FontSize"
                  To="22"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseLeave">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:1"
                  Storyboard.TargetProperty="FontSize"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
View Code

 在這個示例中,使用了一個預先構建的DoubleAnimation類。DoubleAnimation類可以在一段給定的時間內將任何雙精度數值逐漸改變爲設定的目標值。由於雙精度數值以較小的步長改變,因此將會發現字體逐漸的增大。當依賴項屬性等於一個特定值時也能夠執行動畫。若是沒有適合的事件可供使用而又但願執行一個動畫時,須要使用以前介紹的屬性觸發器。不爲屬性觸發器提供任何Setter對象。反而,設置Trigger.EnterActions和Trigger.ExitActions屬性。這兩個屬性都有一個動做集合,例如,啓動一個動畫的BeginStoryboard動做。當屬性到達指定的值時,執行EnterActions,而當屬性離開指定的值時,執行ExitActions。

11.3 行爲

  樣式提供了重用一組屬性設置的實用方法。屬性設置僅僅是用戶界面基礎結構中的小部分。設置在大部分基本的程序一般須要大量的用戶界面代碼,這些代碼與應用程序的功能無關。在許多程序中,用於用戶界面的代碼,不管是在數量仍是複雜性上都超出了業務代碼。許多這類代碼是通用的,這意味着建立的每一個WPF對象中須要編寫相同的內容。因此開發了行爲這一特徵。其想法很簡單:您建立一個封裝了一些通用用戶界面功能的行爲。這一個功能能夠是基本功能,也能夠是負責功能。一旦構建了功能,能夠將其添加到任意應用程序中的另外一個控件中,具體方法是該控件鏈接到適當的行爲並設置行爲的屬性。

11.3.1 獲取行爲支持

  重用用戶界面代碼通用塊的基本機構不是WPF的一部分。反而,它被捆綁到Expression Blend。這是由於行爲開始是做爲Expression Blend的設計時特性引入的。Expression Blend仍然是經過將行爲拖動到須要行爲的控件上來添加行爲的惟一工具。只須要付出不多的努力就能夠在Visual Studio應用程序中建立和使用行爲。只須要手動編寫標記,而不是使用工具箱。爲了得到支持行爲的程序集,有兩種選擇。

  • 安裝Expression Blend
  • 安裝Expression Blend3 SDK

不管採用哪一種方法,在相似c:\Program Files\Microsoft SDKs\Expression\Blend3\Interactivity\Libraries\WPF的文件夾中都將發現兩個重要的程序集:

  • System.Windows.Interativity.dll。這個程序集定義了支持行爲的基本類。它是行爲特徵的基礎。
  • Microsoft.Expression.Inteactions.dll。這個程序集經過添加可選的以核心行爲類爲基礎的動做和觸發器類,增長了一些有用的擴展。

11.3.2 理解行爲模型

  行爲特性具備兩個版本。一個版本針對Silverlight添加行爲支持而設計,另外一個版本是針對WPF設計的。儘管這兩個版本提供了相同的特性,可是行爲特性和Silverlight領域更吻合,由於它彌補了更大的鴻溝。然而,WPF支持觸發器,行爲特性包含本身的觸發器系統,而觸發器系統與WP模型不匹配。相似名稱的這兩個特性有部分重合可是不徹底相同。在WPF中,觸發器最重要的角色是構建靈活的樣式和控件模板。在觸發器的幫助下,樣式和模板能夠變得更加智能;WPF觸發器支持更增強大的樣式和控件模板。而Expression Blend觸發器支持快速的不須要代碼的應用程序設計。對於使用WPF的普通開發人員來講全部這些意味着:

  • 行爲模型不是WPF的核心部分,因此行爲不像樣式和模板那樣肯定。換句話說,能夠編寫不使用行爲的WPF應用程序.
  • 若是在Expression Blend上耗費大量時間,或但願爲其餘Expressioin Blend用戶開發組件,可能會對Expression Blend中的觸發器特性感興趣。儘管和WPF中的觸發器系統使用相同的名稱,可是它們不是相互重疊的,而且能夠同時使用這二者。
  • 若是不使用Expression Blend,能夠徹底濾過其餘觸發器特性,可是仍然應當分析Expressioin Blend提供的功能完整的行爲類。

11.3.3 建立行爲

  行爲旨在封裝一些UI功能,從而能夠不用編寫代碼就可以將其做用到元素上。從另一個角度看,每一個行爲都爲元素提供了一個服務。該服務一般涉及到監聽幾個不一樣的事件並執行幾個相關操做。爲了更好的理解行爲,最好的方法是本身建立一個行爲。假設爲任意元素提供使用鼠標在anvas面板上拖動元素的功能。對於單個元素實現該功能的基本步驟是很是簡單的,代碼監聽屬性事件並修改設置相應Canvas座標的附加屬性。

  首先建立一個WPF類庫程序集。在該程序中,添加對System.Windows.Interactivity.dll程序集的引用。而後,建立一個繼承自Behavior基類的類。Behavior是一個通用類,該類使用一個類型參數。可使用該類型參數將行爲限制到特定的元素,或者可使用UIElement或FrameworkElement將他們都包含進來,以下所示:

 public class DragInCanvasBehavior : Behavior<UIElement>
    {}

在任何行爲中,第一步是覆蓋OnAttached()和OnDetaching()方法。當調用OnAttached()方法時,能夠訪問放置行爲的元素(經過AssociatedObject屬性),而且能夠關聯事件處理程序。當調用OnDetaching()方法時,移除事件處理程序。下面是DragInCanvasBehavior類用於監視MouseLeftButtonDown、MouseMove以及MouseLeftButtonUp事件的代碼:

        protected override void OnAttached()
        {
            base.OnAttached();

            // 鏈接事件處理程序.            
            this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            // 分離事件處理程序。
            this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
        }
View Code

最後一步是在事件處理程序中運行適當的代碼。例如,當用戶單擊鼠標左鍵時,DragInCanvasBehavior開始拖動操做,記錄元素左上角與鼠標指針之間的偏移,並捕獲鼠標:

private Canvas canvas;
// 跟蹤被拖動元素。
        private bool isDragging = false;

        // 當元素被點擊時,記錄的確切位置單擊
        private Point mouseOffset;

        private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 找到畫布
            if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas;

            // 拖動模式開始.
            isDragging = true;

            // 獲得點擊的位置相對於元素(元素的左上角是(0,0)。
            mouseOffset = e.GetPosition(AssociatedObject);

            // 捕獲鼠標。這樣你就會保持receiveing MouseMove事件即便用戶混亂鼠標元素。


            AssociatedObject.CaptureMouse();
        }
View Code

當元素處於拖動模式而且移動鼠標時,重新定位元素:

        private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                // 獲得的元素的位置相對於畫布上。

                Point point = e.GetPosition(canvas);

                // 移動元素。
                AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
                AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
            }
        }
View Code

當釋放鼠標鍵時,結束拖動:

        private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDragging)
            {
                AssociatedObject.ReleaseMouseCapture();
                isDragging = false;
            }
        }
View Code

11.3.4 使用行爲

  建立一個新的WPF應用程序項目。而後添加對定義DragInCanvasBehavior類的類庫以及System.Windows.Interactivity.dll程序集的引用。在XML中映射這兩個名稱空間。假設DragInCanvasBehavior類的類庫名爲CustomBehaviorsLibrary,則須要如下標記:

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    xmlns:custom="clr-namespace:CustomBehaviorsLibrary;assembly=CustomBehaviorsLibrary"

爲了使用這個行爲,只須要使用Interaction.Behaviors附加屬性在Canvas面板中添加任意元素。下面的標記建立了一個具備三個圖形的Canvas面板。兩個Ellipse元素使用了DragInCanvasBehavior,而且能在Canvas面板中拖動。Rectangle元素沒有使用DragInCanvasBehavior,所以沒法移動。

<Canvas>
        <Rectangle Canvas.Left="10" Canvas.Top="10" Fill="Yellow" Width="40" Height="60"></Rectangle>
        <Ellipse Canvas.Left="10" Canvas.Top="70" Fill="Blue" Width="80" Height="60">
            <i:Interaction.Behaviors>
                <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
            </i:Interaction.Behaviors>
        </Ellipse>
        <Ellipse Canvas.Left="80" Canvas.Top="70" Fill="OrangeRed" Width="40" Height="70">
            <i:Interaction.Behaviors>
                <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
            </i:Interaction.Behaviors>
        </Ellipse>
    </Canvas>
View Code

本章節介紹瞭如何使用樣式重用元素的格式化設置,還分析瞭如何使用行爲開發整潔的用戶界面功能包,而後能夠將其鏈接到任意元素。這兩個工提供了製做智能程度更高、具備更好維護性用戶界面的方法——幾種格式化細節和複雜邏輯,而不要求開發人員在整個應用程序中的不一樣位置使用這些細節和邏輯並屢次使用。

相關文章
相關標籤/搜索