天天看點

WPF學習概述

引言

在桌面開發領域,雖然在某些領域,基于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,這是個圓角按鈕。
    WPF學習概述
<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,這是一個點贊按鈕。
    WPF學習概述
<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 模型和模闆、附加屬性的運用,使得自定義控件的用途得到了進一步縮減,隻有當實在萬不得已時,在定義自定義控件。

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

WPF學習概述

例如,在示例代碼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>           

實作了這樣的界面

WPF學習概述

路由事件就是針對這組元素樹中多個元素調用處理程式的事件。當我們點選了按鈕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為觸發事件的源。
WPF學習概述

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

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

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

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

5、處理StackPanel的MouseDown事件。

6、處理Border的MouseDown事件。

總結

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

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