在桌面開發領域,雖然在某些領域,基於electron的跨平臺方案可以爲咱們帶來某些便利,可是因爲WPF技術可以更好的運用Direct3D帶來的性能提高、以及海量Windows操做系統和硬件資源的支持,因此他依然有着得天獨厚的優點。前端
固然,選用一門技術,依然看公司的基因土壤和綜合因素或者老闆的心血來潮,例如QT也一樣是一門很是不錯的跨平臺圖形界面解決方案。git
目前咱們公司在桌面開發領域普遍應用了WPF技術,主要是使用其做爲大屏數據可視化相關的UI呈現,包括一些數據展現效果、動畫效果等。因爲以前我對WPF僅有三週經驗,所以在開發和設計相關功能時,一些簡單功能還能勉強完成,稍微複雜一點的就有點費時過長了,所以這篇文章主要梳理本身的學習筆記,以便總結學習成果。github
在Quote上有人提出了這樣一個相同的問題,查看問題,開發者Srikanth Pagadala如是回答:web
一、以瞭解基礎控件做爲學習的起步過程:這些控件包括TextBox,Button,TextBlock及其餘的,理解這些控件對外提供的屬性,以及如何使用。
二、瞭解和使用佈局空間:例如Grid、StackPanel、DockerPanel和其餘控件,在這一點上,你須要花費大量的時間。同時你須要學會建立複雜的UI設計。
三、瞭解循環類型的空間,例如ItemControl控件。
四、瞭解關於模板的概念。包括如何定義包含CheckBox的Combox,同時這個控件還包含了一張圖片的按鈕,以及如何在ItemsControl中使用不一樣的模板。
五、理解數據綁定的運行機制。嘗試建立一個MVVM或相似類型的應用程序。
六、建立一個典型的控件,探索DependencyProperties(依賴屬性)和AttachedProperties(附加屬性)。
七、建立一個樣式資源,理解如何給控件設計樣式。windows
除此以外,還有其餘開發者給出了補充回答:後端
一、學習控件的數據綁定過程,在DataGrid上實現數據綁定。
二、學習和實現INotifyPropertyChanged類。查看如何實現
三、學習Observable Collection。該類型的集合普遍使用於數據集合綁定方面,同時也提供了數據改變通知的機制。
四、使網格上的列可編輯。用文本控件(用戶項目模板)替換列。爲每一個捕獲文本更改事件的列建立一個屬性。在文本控件上使用綁定類型。嘗試捕獲您在後端在網格上所作的更改。
五、成功將數據控件中的文本控件與後端屬性綁定後,請在同一頁面上建立網格的副本。嘗試同步這兩個網格。例如,您在第一個網格中所作的每一個更改都必須在第二個網格中自動更新。api
網站「https://www.wpf-tutorial.com/」是一個專門用於學習WPF的網站,經過這個網站,能夠快速的入門WPF。數組
因爲WPF技術已經比較熟悉,因此書籍也比較多,網友推薦來自劉鐵猛老師的《深刻淺出WPF》這本書,而我經過Kindle則看到了一本比較有意思的書《葵花寶典-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語法算是該技術的一大特點,也是被學習者視同爲學習路徑陡峭的「罪魁禍首」。緣由是在前端技術飛速發展的今天,HTML的語法體系因爲更早的被開發者接受,因此也天然而然更容易成爲開發者的首選。electron
而XAML是一種脫胎於XML,並吸取了HTML的精華的語法體系,是一種界面描述語言,XML語法自己相對而言較爲臃腫的體系,看似成爲了他的歷史負擔,可是其實倒也沒那麼複雜,經過幾個簡單的示例,其實就足夠掌握這門新的語法體系了。例如,使用這樣的語法,徹底能夠平滑過渡到這樣的語法體系。(部分標籤其實只是大小寫不一樣)。固然,在XAML中熟練編寫樣式,確實須要花一點點時間。
在WPF中,經過XAML定義面向用戶交互層的界面,而後編譯成baml運行,後端則使用C#或VB.NET這樣的CLR語法來實現邏輯交互。
根元素定義是定義XAML的命名空間。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
經過xaml定義按鈕,並設置文本爲 helloworld 。這種寫法在官方文檔中稱爲「屬性語法」,即直接在XAML中對屬性進行設置。
<Button Background="Blue" Foreground="Red" Content="hello world"/>
經過xaml定義按鈕,並設置其背景爲藍色畫筆,字體顏色爲紅色畫筆,內容 爲helloworld。這種寫法在官方文檔中稱爲「屬性元素語法」。
<Button> <Button.Background> <SolidColorBrush Color="Blue"/> </Button.Background> <Button.Foreground> <SolidColorBrush Color="Red"/> </Button.Foreground> <Button.Content> hello world </Button.Content> </Button>
定義按鈕的顏色爲紅色和藍色漸變色,內容爲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語法對 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 的形式來定義其內容,使得開發者可以經過 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>
傳統的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>
在上述事例中,共定義了兩個按鈕的樣式,分別是:
<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>
<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>
經過了解WPF的經常使用控件,咱們能夠知道本身須要使用的控件有哪些屬性,並能使用 XAML 語法對相應的屬性進行設置,這種設置方法有別於經過C#代碼的形式進行定義的方法,在 XAML中的屬性稱爲 「標記」。標記使用 「{}」 花括號,編譯器經過該花括號將語法和XAML語法進行區分。
例如:
HeaderTemplate="{DynamicResource StretchedHeaderTemplate}"
在進行標記值轉換時,有時候須要使用TypeConverters實現類型轉換。例如,在上述示例代碼中,能夠看到使用了字符串「#525252」來定義顏色,在內部就是實現了從字符串到 Color 類的轉換過程。限於篇幅有限,此處就暫時略過。
<object property="{x:Type prefix:typeNameValue}" .../>
<object property="{x:Static prefix:typeName.staticMemberName}" .../>
<object property="{x:Null}" .../>
<x:Array Type="typeName"> arrayContents </x:Array>
<object property="{StaticResource key}" .../>
<object property="{DynamicResource key}" .../>
<object property="{Binding}" .../> -or- <object property="{Binding bindProp1=value1[, bindPropN=valueN]*}" ... /> -or- <object property="{Binding path}" .../> -or <object property="{Binding path[, bindPropN=valueN]*}" .../>
<Binding RelativeSource="{RelativeSource modeEnumValue}" .../>
<object property="{TemplateBinding sourceProperty}" .../>
<object property="{ColorConvertedBitmap imageSource sourceIIC destinationIIC}" .../>
<object x:Key="{ComponentResourceKey {x:Type targetTypeName}, targetID}" .../>
在開發過程當中,咱們能夠直接在按鈕上進行按鈕模板的定義,例以下面的代碼。
<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 的形式引入。可是這樣的引用形式,沒有對象圖的訪問權限,意味着沒法訪問資源依賴的其餘資源。
<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>
將上述代碼中的{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>
一、因爲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 =
附加屬性
按照官方的說法就是「附加屬性旨在用做可在任何對象上設置的一類全局屬性」,例如,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的按鈕點擊事件。
void MakeButton2() { Button b2 = new Button(); b2.Click += new RoutedEventHandler(Onb2Click2); } void Onb2Click2(object sender, RoutedEventArgs e) { //logic to handle the Click event }
一、處理Border根元素的隧道事件PreviewMouseDown
二、處理StackPanel面板的隧道事件PreviewMouseDown.
三、處理Button按鈕的隧道事件的PreMouseDown。
四、處理Button按鈕的MouseDown事件。
五、處理StackPanel的MouseDown事件。
六、處理Border的MouseDown事件。
WPF是一個很是龐大的技術體系,以上學習路徑僅供開發者進行簡單的入門,因爲篇幅有限,對於標記擴展還須要進一步理解透徹,以及格式轉換、圖形繪製、數據綁定、MVVM等內容未能一一描述。
若是果想要對WPF進一步瞭解,最好經過系統的學習相關知識,除了前面提到的網站和幾本書,最好的入門網站依然是微軟官方文檔。