WPF學習筆記(一)

  • 2020 年 3 月 18 日
  • 筆記

引言

在桌面開發領域,雖然在某些領域,基於electron的跨平台方案能夠為我們帶來某些便利,但是由於WPF技術能夠更好的運用Direct3D帶來的性能提升、以及海量Windows操作系統和硬件資源的支持,所以他依然有着得天獨厚的優勢。

當然,選用一門技術,依然看公司的基因土壤和綜合因素或者老闆的心血來潮,例如QT也同樣是一門非常不錯的跨平台圖形界面解決方案。

目前我們公司在桌面開發領域廣泛應用了WPF技術,主要是使用其作為大屏數據可視化相關的UI呈現,包括一些數據展示效果、動畫效果等。由於之前我對WPF僅有三周經驗,因此在開發和設計相關功能時,一些簡單功能還能勉強完成,稍微複雜一點的就有點費時過長了,因此這篇文章主要梳理自己的學習筆記,以便總結學習成果。

如何學習WPF技術?

在Quote上有人提出了這樣一個相同的問題,查看問題,開發者Srikanth Pagadala如是回答:

1、以了解基礎控件作為學習的起步過程:這些控件包括TextBox,Button,TextBlock及其他的,理解這些控件對外提供的屬性,以及如何使用。
2、了解和使用布局空間:例如Grid、StackPanel、DockerPanel和其他控件,在這一點上,你需要花費大量的時間。同時你需要學會創建複雜的UI設計。
3、了解循環類型的空間,例如ItemControl控件。
4、了解關於模板的概念。包括如何定義包含CheckBox的Combox,同時這個控件還包含了一張圖片的按鈕,以及如何在ItemsControl中使用不同的模板。
5、理解數據綁定的運行機制。嘗試創建一個MVVM或類似類型的應用程序。
6、創建一個典型的控件,探索DependencyProperties(依賴屬性)和AttachedProperties(附加屬性)。
7、創建一個樣式資源,理解如何給控件設計樣式。

除此之外,還有其他開發者給出了補充回答:

1、學習控件的數據綁定過程,在DataGrid上實現數據綁定。
2、學習和實現INotifyPropertyChanged類。查看如何實現
3、學習Observable Collection。該類型的集合廣泛使用於數據集合綁定方面,同時也提供了數據改變通知的機制。
4、使網格上的列可編輯。用文本控件(用戶項目模板)替換列。為每個捕獲文本更改事件的列創建一個屬性。在文本控件上使用綁定類型。嘗試捕獲您在後端在網格上所做的更改。
5、成功將數據控件中的文本控件與後端屬性綁定後,請在同一頁面上創建網格的副本。嘗試同步這兩個網格。例如,您在第一個網格中所做的每個更改都必須在第二個網格中自動更新。

網站「https://www.wpf-tutorial.com/」是一個專門用於學習WPF的網站,通過這個網站,可以快速的入門WPF。

由於WPF技術已經比較熟悉,所以書籍也比較多,網友推薦來自劉鐵猛老師的《深入淺出WPF》這本書,而我通過Kindle則看到了一本比較有意思的書《葵花寶典-WPF自學手冊》,這本書寫得比較生動,通過故事的形式講了WPF的許多技術原理,無形中讓我對WPF的概念有了許多新的認識。當然,這本書已經有點年頭了。

WPF的常用控件

控件類型 控件名稱 控件說明 鏈接地址
組件 Window 窗口 查看示例
Page 頁面 查看示例
NavigationWindow 導航窗口 查看示例
Frame 查看示例
常規控件 Button 按鈕控件,提供Content作為內容 查看示例
TextBox 文本框控件,用以輸入文本 查看示例
TextBlock 文本塊,用以顯示文本 查看示例
Label 標籤,用以顯示文本 查看示例
ProgressBar 進度條 查看示例
ToggleButton 一種可以設置開關三態的按鈕 查看示例
Image 圖像控件,通過Source設置資源路徑 查看示例
CheckBox 勾選框,可以設置是否勾選的三種狀態 查看示例
RichTextBox 富文本框,可以多種格式顯示和輸入文本 查看示例
TreeView 樹視圖,以樹狀圖的形式顯示綁定內容,可以顯示是否勾選三態。 查看示例
WebBrowser 瀏覽器,基於IE內核的瀏覽器控件 查看示例
Calendar 日曆控件 查看示例
ComboBox 下拉列表 查看示例
ContentControl 內容控件 查看示例
Expander 擴展器,可以顯示和摺疊面板內的元素 查看示例
GroupBox 分組框 查看示例
StatusBar 狀態欄,用於在頁面下方顯示狀態信息。 查看示例
DateTimePicker 時間控件,可以設置時間狀態。 查看示例
DocumentViewer 文檔查看器 查看示例
RadioButton 單選按鈕 查看示例
ScollViewer 滾動視圖 查看示例
ScollBar 滾動條 查看示例
Separator 分隔器 查看示例
ToolBar 工具條 查看示例
Slider 查看示例
Menu 菜單 查看示例
MediaElement 多媒體控件 查看示例
PasswordBox 密碼輸入框 查看示例
TabControl 選項卡 查看示例
ToolBarTray 工具條 查看示例
WindowsFormsHost 用以承載WinForm 查看示例
Border 邊框 查看示例
數據控件 ListView 列表視圖 查看示例
DataGrid 數據表 查看示例
ListBox 列表框 查看示例
布局 WrapPanel 可變面板 查看示例
StackPanel 固定面板 查看示例
DockerPanel 停靠面板 查看示例
Grid 表格布局 查看示例
UniformGrid 統一分佈表格布局 查看示例
查看示例 Canvas 畫布 查看示例
圖形 Point 查看示例
Line 查看示例
Path 路徑 查看示例
Polygon 多邊形 查看示例
Polyline 多段線 查看示例
Rectangle 矩形 查看示例
Shape 畫筆 查看示例
Rectangle 矩形 查看示例
Ellipse 橢圓 查看示例

WPF的XAML語法

概述

在WPF技術中引入的XAML語法算是該技術的一大特色,也是被學習者視同為學習路徑陡峭的「罪魁禍首」。原因是在前端技術飛速發展的今天,HTML的語法體系由於更早的被開發者接受,所以也自然而然更容易成為開發者的首選。

而XAML是一種脫胎於XML,並吸收了HTML的精華的語法體系,是一種界面描述語言,XML語法本身相對而言較為臃腫的體系,看似成為了他的歷史負擔,但是其實倒也沒那麼複雜,通過幾個簡單的示例,其實就足夠掌握這門新的語法體系了。例如,使用這樣的語法,完全可以平滑過渡到這樣的語法體系。(部分標籤其實只是大小寫不同)。當然,在XAML中熟練編寫樣式,確實需要花一點點時間。

在WPF中,通過XAML定義面向用戶交互層的界面,然後編譯成baml運行,後端則使用C#或VB.NET這樣的CLR語法來實現邏輯交互。

XAML的語法定義

XAML的根元素定義

根元素定義是定義XAML的命名空間。

<Page    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

XAML的屬性語法

通過xaml定義按鈕,並設置文本為 helloworld 。這種寫法在官方文檔中稱為「屬性語法」,即直接在XAML中對屬性進行設置。

<Button Background="Blue" Foreground="Red" Content="hello world"/>

XAML的屬性元素語法

通過xaml定義按鈕,並設置其背景為藍色畫筆,字體顏色為紅色畫筆,內容 為helloworld。這種寫法在官方文檔中稱為「屬性元素語法」。

<Button>    <Button.Background>      <SolidColorBrush Color="Blue"/>    </Button.Background>    <Button.Foreground>      <SolidColorBrush Color="Red"/>    </Button.Foreground>    <Button.Content>      hello world    </Button.Content>  </Button>

XAML的集合語法

定義按鈕的顏色為紅色和藍色漸變色,內容為helloworld。這種稱為「集合語法」。

<LinearGradientBrush>    <LinearGradientBrush.GradientStops>      <!-- no explicit new GradientStopCollection, parser knows how to find or create -->      <GradientStop Offset="0.0" Color="Red" />      <GradientStop Offset="1.0" Color="Blue" />    </LinearGradientBrush.GradientStops>  </LinearGradientBrush>

XAML的樣式定義

通過屬性語法來定義按鈕的外觀

樣式定義使用

標籤,然後在中間對樣式的內容進行定義。

例如,以下表示通過XAML語法對 ToggleButton 按鈕定義了一個命名為 ToggleLikeButtonStyle 的樣式。

 <Style TargetType="ToggleButton" x:Key="ToggleLikeButtonStyle">              <Setter Property="Margin"   Value="4" />                <Setter Property="FontWeight" Value="Black"/>                <Setter Property="Foreground"  Value="Black" />                <Setter Property="BorderThickness" Value="0"/>                <Setter Property="IsThreeState" Value="False"></Setter>    </Style>

WPF中的模板Template

WPF中的控件可以通過模板 Template 的形式來定義其內容,使得開發者能夠通過 XAML 靈活的對控件的外觀進行擴展。例如,如下定義了一個 Template,這個控件模板將會對控件(Button)定義填充制定顏色。

 <Setter Property="Template">                  <Setter.Value>                      <ControlTemplate>                          <Border BorderThickness="0" CornerRadius="3">                              <Border.Background>                                  <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                      <GradientStop Color="#4696F2" Offset="0.5"/>                                  </LinearGradientBrush>                              </Border.Background>                              <ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />                          </Border>                      </ControlTemplate>                  </Setter.Value>              </Setter>  

XAML中的觸發器Triggers

傳統的WinForm開發者習慣於通過事件的機制對按鈕的外觀進行定義,而在WPF中,則可以通過屬性的形式對外觀進行設置,這使得開發者更能夠寫出高質量的代碼。

例如,如下代碼通過定義觸發器,設置控件(控件為 ToggleButton),當控件的勾選狀態屬性為「IsChecked」 時,其邊框填充色為#4696F2顏色。

<ControlTemplate.Triggers>                              <Trigger Property="IsChecked" Value="True">    <Setter Property="Border.Background" TargetName="PART_Background"  <Setter.Value>                                          <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                              <GradientStop Color="#4696F2" Offset="0.5"/>                                          </LinearGradientBrush>                                      </Setter.Value>                                  </Setter>                                  <Setter Property="Width" TargetName="PART_Background" Value="60"></Setter>                                  <Setter Property="Content"  TargetName="contextPresenter" Value="已點贊"></Setter>                                  <Setter Property="Visibility"  TargetName="contextPresenter" Value="Visible"></Setter>                                  <Setter Property="Visibility" TargetName="contextImage" Value="Hidden"/>                              </Trigger>  </ControlTemplate.Triggers>                                                              

部分完整代碼

在上述事例中,共定義了兩個按鈕的樣式,分別是:

  • FlatButtonStyle,這是個圓角按鈕。
<Style TargetType="Button" x:Key="FlatButtonStyle">              <Setter Property="Margin"   Value="4" />              <Setter Property="FontWeight" Value="Black"/>              <Setter Property="Foreground"  Value="Black" />              <Setter Property="BorderThickness" Value="0"/>              <Setter Property="Template">                  <Setter.Value>                      <ControlTemplate>                          <Border BorderThickness="0" CornerRadius="3">                              <Border.Background>                                  <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                      <GradientStop Color="#4696F2" Offset="0.5"/>                                  </LinearGradientBrush>                              </Border.Background>                              <ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />                          </Border>                      </ControlTemplate>                  </Setter.Value>              </Setter>          </Style>
  • ToggleLikeButtonStyle,這是一個點贊按鈕。
<Style TargetType="ToggleButton" x:Key="ToggleLikeButtonStyle">              <Setter Property="Margin"   Value="4" />              <Setter Property="FontWeight" Value="Black"/>              <Setter Property="Foreground"  Value="Black" />              <Setter Property="BorderThickness" Value="0"/>              <Setter Property="IsThreeState" Value="False"></Setter>              <Setter Property="Template">                  <Setter.Value>                      <ControlTemplate TargetType="{x:Type ToggleButton}">                          <Border BorderThickness="0" CornerRadius="3" Name="PART_Background">                              <Border.Background>                                  <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                      <GradientStop Color="#525252" Offset="0.5"/>                                  </LinearGradientBrush>                              </Border.Background>                              <Grid>                                  <ContentPresenter x:Name="contextPresenter" Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />                                  <Image x:Name="contextImage" Width="24" Height="24" Source="assests/thumbs-up-outline.png" HorizontalAlignment="Center" VerticalAlignment="Center"/>                              </Grid>                          </Border>                          <ControlTemplate.Triggers>                              <Trigger Property="IsChecked" Value="True">                                  <Setter Property="Border.Background" TargetName="PART_Background">                                      <Setter.Value>                                          <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                              <GradientStop Color="#4696F2" Offset="0.5"/>                                          </LinearGradientBrush>                                      </Setter.Value>                                  </Setter>                                  <Setter Property="Width" TargetName="PART_Background" Value="60"></Setter>                                  <Setter Property="Content"  TargetName="contextPresenter" Value="已點贊"></Setter>                                  <Setter Property="Visibility"  TargetName="contextPresenter" Value="Visible"></Setter>                                  <Setter Property="Visibility" TargetName="contextImage" Value="Hidden"/>                              </Trigger>                              <Trigger Property="IsChecked" Value="False">                                  <Setter Property="Border.Background" TargetName="PART_Background">                                      <Setter.Value>                                          <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                              <GradientStop Color="#525252" Offset="0.5"/>                                          </LinearGradientBrush>                                      </Setter.Value>                                  </Setter>                                  <Setter Property="Width" TargetName="PART_Background" Value="40"></Setter>                                  <Setter Property="Visibility"  TargetName="contextPresenter" Value="Hidden"></Setter>                                  <Setter Property="Visibility" TargetName="contextImage" Value="Visible"/>                              </Trigger>                          </ControlTemplate.Triggers>                      </ControlTemplate>                  </Setter.Value>              </Setter>  </Style>

XAML的標記擴展

通過了解WPF的常用控件,我們可以知道自己需要使用的控件有哪些屬性,並能使用 XAML 語法對相應的屬性進行設置,這種設置方法有別於通過C#代碼的形式進行定義的方法,在 XAML中的屬性稱為 「標記」。標記使用 「{}」 花括號,編譯器通過該花括號將語法和XAML語法進行區分。

例如:

 HeaderTemplate="{DynamicResource StretchedHeaderTemplate}"  

標記值的轉換與TypeConverters

在進行標記值轉換時,有時候需要使用TypeConverters實現類型轉換。例如,在上述示例代碼中,可以看到使用了字符串「#525252」來定義顏色,在內部就是實現了從字符串到 Color 類的轉換過程。限於篇幅有限,此處就暫時略過。

XAML中內置特殊標記擴展

  • x:Type:特定類型
<object property="{x:Type prefix:typeNameValue}" .../>    
  • x:Static:使用靜態值。
<object property="{x:Static prefix:typeName.staticMemberName}" .../>    
  • x:Null:使用空對象定義為屬性值。
<object property="{x:Null}" .../>    
  • x:Array:使用數組對象。
<x:Array Type="typeName">    arrayContents  </x:Array>    

常見的標記擴展

  1. StaticResource:通過替換已定義資源的值來為屬性提供內容,該資源標記在XAML加載時自動執行。靜態資源無法通過在XAML語法體系中對其引用關係進行前向引用,意味着無法通過多層級關係定義可復用的樣式資源,如果需要這樣做,則需要使用DynamicResource。
<object property="{StaticResource key}" .../>    
  1. DynamicResource:在運行時為資源提供內容。
<object property="{DynamicResource key}" .../>    
  1. Binding:在運行時為使用數據上下文為數據提供內容。
<object property="{Binding}" .../>  -or-  <object property="{Binding  bindProp1=value1[, bindPropN=valueN]*}" ...  />  -or-  <object property="{Binding path}" .../>  -or  <object property="{Binding path[, bindPropN=valueN]*}" .../>    
  1. RelativeSource:提供了可在運行時對象樹中導航幾個可能的關係的 Binding 的源信息。
<Binding RelativeSource="{RelativeSource modeEnumValue}" .../>  
  1. TemplateBinding:使控件模板能夠使用模板化屬性的值,這些屬性來自於將使用該模板的類的對象模型定義屬性。
<object property="{TemplateBinding sourceProperty}" .../>    
  1. ColorConvertedBitmap:提供一種方法,用於指定沒有嵌入的配置文件的位圖源。 顏色上下文/配置文件由 URI 指定,與映像源 URI 相同。
<object property="{ColorConvertedBitmap imageSource sourceIIC destinationIIC}" .../>    
  1. ComponentResourceKey和TemplateResourceKey:
<object x:Key="{ComponentResourceKey {x:Type targetTypeName}, targetID}" .../>    

XAML資源復用

在開發過程中,我們可以直接在按鈕上進行按鈕模板的定義,例如下面的代碼。

<Button Width="40" Height="40" Style="{DynamicResource CubeImageButtonStyle}" Click="Button_Click" Content="點贊">      <Button.Background>                                  <ImageBrush ImageSource="/assests/favicon.png" Stretch="Fill"/>                              </Button.Background>                          </Button>                            <Setter Property="Template"                  <Setter.Value>                      <ControlTemplate>                          <Border BorderThickness="0" CornerRadius="3">                              <Border.Background>                                  <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">                                      <GradientStop Color="#4696F2" Offset="0.5"/>                                  </LinearGradientBrush>                              </Border.Background>                              <ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />                          </Border>                      </ControlTemplate>                  </Setter.Value>              </Setter>          </Style>   </Window.Resources>   <Grid>  <Button Grid.Column="0" Grid.Row="1" Style="{StaticResource FlatButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="48" Height="16" FontSize="10" Background="#4696F2" Content="獲取"></Button>   </Grid>                          

這樣的代碼在界面比較簡單時,還無所謂,但是隨着控件的樣式越來越複雜,可能會成為一團亂麻,這對於追求優雅代碼的我們來說,可能是難以忍受的,所以往往會使用資源引用來完成。

StaticResource

例如,我們可以在當前頁面代碼中定義對應的樣式,這種樣式可以使用 StaticResource 的形式引入。但是這樣的引用形式,沒有對象圖的訪問權限,意味着無法訪問資源依賴的其他資源。

 <Window.Resources>  <Style TargetType="Button" x:Key="FlatButtonStyle">               <Setter Property="Margin"   Value="4" />              <Setter Property="FontWeight" Value="Black"/>              <Setter Property="Foreground"  Value="Black" />                      <Setter Property="BorderThickness" Value="0"/>  </Window.Resources>          

DynamicResource

將上述代碼中的{StaticResource FlatButtonStyle} 改成{StaticResource FlatButtonStyle}則會在運行時加載樣式,並可以訪問相應的對象圖。

當然,這樣的更改意義不大,如果該FlatButtonStyle引用了其他樣式或元素,會發生作用。

<Grid  <Button Grid.Column="0" Grid.Row="1" Style="{StaticResource FlatButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="48" Height="16" FontSize="10" Background="#4696F2" Content="獲取"></Button>   </Grid>

注意事項

1、由於XAML語法脫胎於XML語法,而XML語法中本身對某些輸入字符,如「<>」存在限制,所以在XAML中也會出現這類問題,並會被Visual Studio檢測出錯誤而無法編譯,需要使用UTF-8編碼進行轉換。

用戶控件和自定義控件

用戶控件

而用戶控件,使用於控件組合的場景。

自定義控件

在筆者進行開發時,總是思考究竟是使用用戶控件,還是自定義控件,後來在閱讀《葵花寶典-WPF自學手冊》這本書中,終於得以大徹大悟。

作者指出:「不要被控件的外觀所欺騙,要考慮其內在本質」。即思考控件的基本特徵,首先想到該控件的行為與原有控件的行為是否相似,如果能夠找到,則修改原有控件,而不是定義一個控件。尤其是在XAML語法中,能夠通過Content 模型和模板、附加屬性的運用,使得自定義控件的用途得到了進一步縮減,只有當實在萬不得已時,在定義自定義控件。

作者給出了使用自定義控件的分析思路:

例如,在示例代碼ToggleLikeButtonStyle 中,我實現了一個點贊和取消點贊的狀態,則使用了ToggleButton來完成,就沒必要使用 Button 擴展出一個是否點贊的狀態了。

而如果我們需要實現的功能有這麼複雜,那大概使用傳統的控件就無法實現,就得使用自定義控件了。(點擊查看示例代碼

作者定義了自定義控件 ButtonEx,並實現了依賴屬性 ButtonType,見【依賴屬性】,並定義了不同類型的樣式特徵。

 <Trigger Property="ButtonType" Value="Icon">                  <Setter Property="Cursor" Value="Hand"/>                  <Setter Property="Template">                      <Setter.Value>                          <ControlTemplate TargetType="{x:Type controls:ButtonEx}">                              <Border Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">                                  <Image x:Name="Img" VerticalAlignment="Center" HorizontalAlignment="Center" Source="{TemplateBinding Icon}" Stretch="None"/>                              </Border>                              <ControlTemplate.Triggers>                                  <Trigger Property="IsMouseOver" Value="True">                                      <Setter Property="Opacity" Value="0.8"/>                                  </Trigger>                                  <Trigger Property="IsPressed" Value="True">                                      <Setter Property="Opacity" Value="0.9"/>                                  </Trigger>                              </ControlTemplate.Triggers>                          </ControlTemplate>                      </Setter.Value>                  </Setter>              </Trigger>

使用時,只需這樣設置,即可實現不同類型的按鈕外觀。

 <controls:ButtonEx Icon="/Images/search.png"  Margin="10" ButtonType="Icon"/>

屬性和事件

依賴屬性

依賴屬性是為既有WPF控件對象定義自定義屬性,以便支持其擴展,例如在上述自定義控件的示例中,就定義了依賴屬性 ButtonType,實現了不同類型的按鈕外觀。

public ButtonType ButtonType          {              get { return (ButtonType)GetValue(ButtonTypeProperty); }              set { SetValue(ButtonTypeProperty, value); }          }              public static readonly DependencyProperty ButtonTypeProperty =

            DependencyProperty.Register("ButtonType", typeof(ButtonType), typeof(ButtonEx), new PropertyMetadata(ButtonType.Normal));

附加屬性
按照官方的說法就是「附加屬性旨在用作可在任何對象上設置的一類全局屬性」,例如,DockPanel面板中的子對象,繼承了來自於容器對象的附加屬性,使得其能夠在父對象中實現停靠的功能。

<DockPanel>    <CheckBox DockPanel.Dock="Top">Hello</CheckBox>  </DockPanel>

路由事件

基本定義

假設我們定義了幾個這樣的控件。

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">    <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">      <Button Name="YesButton" Width="Auto" >Yes</Button>      <Button Name="NoButton" Width="Auto" >No</Button>      <Button Name="CancelButton" Width="Auto" >Cancel</Button>    </StackPanel>  </Border>

實現了這樣的界面

路由事件就是針對這組元素樹中多個元素調用處理程序的事件。當我們點擊了按鈕Button時,將會觸發 Button=>StackPanel=>Border的事件路由,而不是像WinForm應用一樣,只能觸發最上層的Button的按鈕點擊事件。

路由策略

  • 冒泡事件(官方稱為浮升,這個翻譯有點。。):調用事件源上的事件處理程序。 路由事件隨後會路由到後續的父級元素,直到到達元素樹的根。 大多數路由事件都使用浮升路由策略。 浮升路由事件通常用於報告來自不同控件或其他 UI 元素的輸入或狀態變化。
  • 直接: 只有源元素本身才有機會調用處理程序以進行響應。通過使用 EventSetter 和 EventTrigger使用來設置處理程序。例如,可以使用RoutedEventArgs的Handled,設置為 true 將事件標記為已處理,將 "停止" 路由用於隧道路由或冒泡路由。
void MakeButton2()  {    Button b2 = new Button();    b2.Click += new RoutedEventHandler(Onb2Click2);  }  void Onb2Click2(object sender, RoutedEventArgs e)  {    //logic to handle the Click event  }
  • 隧道:最初將調用元素樹的根處的事件處理程序。 隨後,路由事件將朝着路由事件的源節點元素(即引發路由事件的元素)方向,沿路由線路傳播到後續的子元素。
  • WPF中約定,隧道路由事件的名稱以單詞「Preview」開頭。 輸入事件通常成對出現,一個是浮升事件,另一個是隧道事件。例如,如下圖所示,假設按鈕2為觸發事件的源。

圖片

1、處理Border根元素的隧道事件PreviewMouseDown

2、處理StackPanel面板的隧道事件PreviewMouseDown.

3、處理Button按鈕的隧道事件的PreMouseDown。

4、處理Button按鈕的MouseDown事件。

5、處理StackPanel的MouseDown事件。

6、處理Border的MouseDown事件。

總結

WPF是一個非常龐大的技術體系,以上學習路徑僅供開發者進行簡單的入門,由於篇幅有限,對於標記擴展還需要進一步理解透徹,以及格式轉換、圖形繪製、數據綁定、MVVM等內容未能一一描述。

如果果想要對WPF進一步了解,最好通過系統的學習相關知識,除了前面提到的網站和幾本書,最好的入門網站依然是微軟官方文檔