引言
在桌面開發領域,雖然在某些領域,基于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>
常見的标記擴充
- StaticResource:通過替換已定義資源的值來為屬性提供内容,該資源标記在XAML加載時自動執行。靜态資源無法通過在XAML文法體系中對其引用關系進行前向引用,意味着無法通過多層級關系定義可複用的樣式資源,如果需要這樣做,則需要使用DynamicResource。
<object property="{StaticResource key}" .../>
- DynamicResource:在運作時為資源提供内容。
<object property="{DynamicResource key}" .../>
- Binding:在運作時為使用資料上下文為資料提供内容。
<object property="{Binding}" .../>
-or-
<object property="{Binding bindProp1=value1[, bindPropN=valueN]*}" ...
/>
-or-
<object property="{Binding path}" .../>
-or
<object property="{Binding path[, bindPropN=valueN]*}" .../>
- RelativeSource:提供了可在運作時對象樹中導航幾個可能的關系的 Binding 的源資訊。
<Binding RelativeSource="{RelativeSource modeEnumValue}" .../>
- TemplateBinding:使控件模闆能夠使用模闆化屬性的值,這些屬性來自于将使用該模闆的類的對象模型定義屬性。
<object property="{TemplateBinding sourceProperty}" .../>
- ColorConvertedBitmap:提供一種方法,用于指定沒有嵌入的配置檔案的位圖源。 顔色上下文/配置檔案由 URI 指定,與映像源 URI 相同。
<object property="{ColorConvertedBitmap imageSource sourceIIC destinationIIC}" .../>
- 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進一步了解,最好通過系統的學習相關知識,除了前面提到的網站和幾本書,最好的入門網站依然是微軟官方文檔。