WPF——樣式和行為


  如果局限於簡單的、灰色外觀的普通按鈕以及其他常用控件,WPF將是沒有新意的捆綁。WPF提供了幾個特性,允許為基本元素插入一些自己的愛好,並標准化應用程序的可視化外觀。本次主要學習樣式和行為。樣式是組織和重用格式化選項的重要工具。不是使用重復的標記填充XAML,以設置注入外邊距、內邊距、顏色以及字體等細節,而可以創建一系列封裝所有這些細節的;樣式。然后可以在需要之處通過一個屬性應用樣式。行為是一個重用用戶界面代碼的更有挑戰性的工具。其基本思想是行為封裝了一些通用的UI功能。如果具有適當的行為,可以使用一兩行XAML標記將其附加到一個元素,從而可以為您節省便攜盒調試代碼的工作。

11.1 樣式基礎

  樣式是可以應用於元素的屬性值的集合。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>

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

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

這種方式雖然將字體細節移除了標記,但還存有問題,1、除惡資源名稱相似之外,沒有明確指明三個資源是相關的。如果需要設置更多的字體屬性,或者決定為不同類型的元素維護不同字體設置,這將變的復雜。2、需要使用資源的標記非常繁瑣。我們可以通過定義一個將所有字體捆綁在一起的自定義類。樣式對這一問題提供了非常好的解決方案。可以定義一個獨立的包裝所有希望設置的屬性的樣式。如下:

  <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 創建樣式對象

  如果希望創建具有更精細目標的樣式,可以使用容器的Resoures集合定義樣式,如StakPanel面板或Grid面板。如果希望在應用程序中重用樣式,可以使用應用程序的Resources集合定義樣式。嚴格來講,不需要同時使用樣式和資源。因為無法與其他元素共享該樣式,如果只是使用樣式設置一些屬性,直接設置熟悉更加容易。但是,可以使用該方法為一個元素關聯觸發器。通過該方法還可以修改元素控件模板的一部分。

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

本章節介紹了如何使用樣式重用元素的格式化設置,還分析了如何使用行為開發整潔的用戶界面功能包,然后可以將其連接到任意元素。這兩個工提供了制作智能程度更高、具有更好維護性用戶界面的方法——幾種格式化細節和復雜邏輯,而不要求開發人員在整個應用程序中的不同位置使用這些細節和邏輯並多次使用。


免責聲明!

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



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