日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

准备.Net转前端开发-WPF界面框架那些事,UI快速实现法

發布時間:2025/3/21 HTML 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 准备.Net转前端开发-WPF界面框架那些事,UI快速实现法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題外話

??? 打開博客園,查看首頁左欄的”推薦博客”,排名前五的博客分別是(此處非廣告):Artech、小坦克、圣殿騎士、騰飛(Jesse)、數據之巔。再看看它們博客的最新更新時間:Artech(2014-08-07) 、小坦克(2012-02-13)、圣殿騎士(2015-06-30)、騰飛(Jesse)(2013-12-18) 、數據之巔(2016-02-19) 。雖然數據之巔在最近發表了一篇博客,但是再看看倒數第二篇,更新時間是2015-11-18。從數據的現象看感覺現在技術大牛們博客更新的越來越少,當然隨著技術沉淀到一定程度,大牛們可能各自的重點不會放在只研究技術上面。但是,我想說的是,現在我們能深入學習的資源確實越來越少了。最近幾年各種技術處在飛速發展中,浮躁的程序猿越來越多,大部分人都是處在熟練使用各種框架的程度,而能靜下心來研究某些框架的具體實現少之甚少。往往比較大型的公司,真正需要的人卻是那些不僅懂框架而且能分析框架的人。題外話就說到這里吧。。。

顯示效果圖

??? 先簡單羅列下這次系統寫的幾個簡單控件。首先聲明展示效果的目的是實現功能,請直接忽略各自的美觀感受。首先是一個登錄界面,效果如下:

??? MessageBox效果:

??? Window、可關閉Item的TabControl、可停靠的DockingPanel效果:

基礎知識

Style

??? 如果了解HTML和CSS,那么WPF的Style的學習起來也比較容易,Style的設計思想就是仿照CSS來實現的。首先我們看看WPF中一個Button實現:

<Button FontSize=”22” Background=”Purple” Foreground=”White” Height=”50” Width=”50” RenderTransformOrigin=”.5,.5”>

??? 一個Button的代碼和HTML元素內嵌樣式非常相似:

<Button Style="width:60px;heigth:50px;color:white;background:purple;font-size:22px" />

??? 我們知道THML可通過CSS把樣式提取出去,WPF中的Style同樣也可以。在一個Window界面,我們可以在Window.Resources中添加獨立樣式:

<Style x:Key=”buttonStyle”><Setter Property=”Button.FontSize” Value=”22”/><Setter Property=”Button.Background” Value=”Purple”/><Setter Property=”Button.Foreground” Value=”White”/><Setter Property=”Button.Height” Value=”50”/><Setter Property=”Button.Width” Value=”50”/><Setter Property=”Button.RenderTransformOrigin” Value=”.5,.5”/> </Setter> </Style>

?? 在聲明Style時,可標記TargetType聲明樣式應用到的元素類型。例如:

<Style x:Key=”buttonStyle” TargetType=”{x:Type Button}”>

??? 一般的Style我們都會指定唯一的Key值,但有時候我們可以不指定Key,直接創建隱式樣式。例如:

<Style TargetType=”{x:Type Button}”>

??? 需要說明的是,這里沒有指定Key并不是說該Style沒有key值,其實在編譯過程中,這總情況的Style都會默認按照TargetType的類型默認指定一個Key值。并且應用到所有類型為TargetType的元素上。

??? 在實現Style時,一般都會涉及到觸發器,觸發器包括:Property triggers、Data triggers、Event triggers、MultiTrigger。接下來分別介紹下這四個觸發器。

??? (1)Property Trigger:只能應用到依賴屬性上,當某個依賴屬性的值的滿足某些條件就會觸發某些改變。先看看代碼:

<Style x:Key=”buttonStyle” TargetType=”{x:Type Button}”> <Style.Triggers><Trigger Property=”IsMouseOver” Value=”True”><Setter Property=”RenderTransform”><Setter.Value><RotateTransform Angle=”10”/></Setter.Value></Setter><Setter Property=”Foreground” Value=”Black”/></Trigger> </Style.Triggers> </Style>

?? 上面的觸發器實現的功能是,當鼠標移到Button上,字體顏色為Black,并且旋轉10度。特別要注意的是,和HTML元素a:hover功能相似,觸發器也有回置的功能,就是說當鼠標移出Button時,Button的字體顏色和旋轉角度恢復到初始值。

??? (2)DataTrigger:和Property Trigger非常相似,但除了應用到依賴屬性外,DataTrigger可應用到任意的.Net屬性上。DataTrigger的代碼寫法和PropertyTrigger還是有區別的,它是通過Bingding來綁定需要判斷的屬性。例如:

<StackPanel Width=”200”><StackPanel.Resources><Style TargetType=”{x:Type TextBox}”> <Style.Triggers><DataTriggerBinding =”{Binding RelativeSource={RelativeSource Self}, Path= Text}”Value=”disabled”><Setter Property=”IsEnabled” Value=”False”/></DataTrigger></Style.Triggers><Setter Property=”Background”Value=”{Binding RelativeSource={RelativeSource Self}, Path=Text}”/></Style></StackPanel.Resources><TextBox Margin=”3”/> </StackPanel>

??? TextBox的Text屬性是一個非依賴屬性,如果Text的值為disabled,設置TextBox的IsEanbled為false。DataTrigger是通過Binding來綁定TextBox的Text屬性。

??? (3)EventTrigger:當元素某些事件被觸發時,可執行某些動畫或者元素的某些屬性發生變化,這些動作可通過EventTrigger實現。看看下面的代碼:

<Button.Triggers><EventTrigger RoutedEvent=”Button.Click”><EventTrigger.Actions><BeginStoryboard><Storyboard TargetProperty=”Width”><DoubleAnimation From=”50” To=”100”Duration=”0:0:5” AutoReverse=”True”/></Storyboard></BeginStoryboard></EventTrigger.Actions></EventTrigger> </Button.Triggers>

??? 上面的代碼實現功能是,當用戶點擊按鈕時,觸發一個動畫,按鈕的Width在5秒鐘的時間內從50增加到100個像素。

??? (4)MultiTrigger:前面介紹的觸發器都是當元素的某一個屬性或者事件觸發時操作。有些時候,我們需要判斷多個條件是否同時滿足,才執行某個操作。這個功能可通過MultiTrigger實現。例如,我們的Button按鈕,當鼠標移到上面并且獲取焦點時,設置字體顏色為Black和旋轉度為10度。實現代碼如下:

<Style.Triggers><MultiTrigger><MultiTrigger.Conditions><Condition Property=”IsMouseOver” Value=”True”/><Condition Property=”IsFocused” Value=”True”/></MultiTrigger.Conditions><Setter Property=”RenderTransform”><Setter.Value><RotateTransform Angle=”10”/></Setter.Value></Setter><Setter Property=”Foreground” Value=”Black”/></MultiTrigger> </Style.Triggers>

Templates

??? 常常在設計一個系統時,都會實現一套自己的UI界面,有些時候我們不得不重寫樣式比較簡單的WPF Window界面。Template給開發者提供完全重寫界面控件的技術,例如我們可以實現自己的窗口TitleBar,并且在上面添加菜單功能。我們在重寫了控件UI的同時也保留了控件的各種功能。模板在分割可視化和邏輯上也功不可沒,例如一些界面的樣式交互效果我們完全可以在模板中實現,而源代碼部分完全只考慮業務邏輯。我們看看下面一個常規的Button樣式模板代碼:

<ControlTemplate x:Key=”buttonTemplate” TargetType=”{x:Type Button}”><Grid><Ellipse x:Name=”outerCircle” Width=”100” Height=”100”><Ellipse.Fill><LinearGradientBrush StartPoint=”0,0” EndPoint=”0,1”><GradientStop Offset=”0” Color=”Blue”/><GradientStop Offset=”1” Color=”Red”/></LinearGradientBrush></Ellipse.Fill></Ellipse><Ellipse Width=”80” Height=”80”><Ellipse.Fill><LinearGradientBrush StartPoint=”0,0” EndPoint=”0,1”><GradientStop Offset=”0” Color=”White”/><GradientStop Offset=”1” Color=”Transparent”/></LinearGradientBrush></Ellipse.Fill></Ellipse></Grid><ControlTemplate.Triggers><Trigger Property=”IsMouseOver” Value=”True”><Setter TargetName=”outerCircle” Property=”Fill” Value=”Orange”/></Trigger><Trigger Property=”IsPressed” Value=”True”><Setter Property=”RenderTransform”><Setter.Value><ScaleTransform ScaleX=”.9” ScaleY=”.9”/></Setter.Value></Setter><Setter Property=”RenderTransformOrigin” Value=”.5,.5”/></Trigger></ControlTemplate.Triggers> </ControlTemplate>

??? 上面的代碼完全重寫了Button的默認樣式,默認的Button是一個矩形,而現在變成了一個Ellipse橢圓形。并且使用漸變的顏色填充橢圓。我們知道默認的Button當我們鼠標移上去或者被單擊時都會有樣式變化。所以,我們還得給Button添加觸發器,這里我們使用的是Property Trigger。當鼠標移到Button上(IsMouseOver=true),設置橢圓的填充顏色為Orange。當按鈕被按下時(IsPressed=true),改變漸變顏色的起始位置。

Theme

??? 一個比較完善的系統都提供了切換系統主題的功能, 我們就拿Window 10系統來說,看看下面的截圖:

??? Window 10系統為用戶提供了很多主題選擇,當我們選擇不同的主題,系統的菜單或者窗口的顏色都會發生變化,但是界面的結構是沒有改變的。WPF的主題也完全一樣。當界面開發人員重寫玩控件的模板后,一般都會把模板中寫某些資源提取出來,放到公共的資源文件中去。這些被提取出來的資源一般包括:界面背景色、邊框顏色、字體顏色、以及圖片等。我們先看一個TabControl的自定義模板:

<Style x:Key="CustomTabControlStyle" TargetType="{x:Type control:CustomTabControl}" BasedOn="{StaticResource {x:Type TabControl}}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type control:CustomTabControl}"><DockPanel LastChildFill="True"><Border DockPanel.Dock="Top" Background="{DynamicResource Tab_Control_Background_Normal}"BorderBrush="{DynamicResource CustomControlBorderBrush}"BorderThickness="1, 1, 1, 0"><TabPanel Margin="0,0,0,0" IsItemsHost="True" /></Border><Border Background="White" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1"> <ContentPresenter ContentSource="SelectedContent" /></Border></DockPanel></ControlTemplate></Setter.Value></Setter><Style.Triggers>...</Style.Triggers></Style>

??? 代碼中,Border的背景色動態的使用了Tab_Control_Background_Normal資源,邊框顏色使用CustomControlBorderBrush資源。而這些被引用的資源都是單獨存放在一個資源文件中。這個資源文件的部分代碼如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><!-- Common Style --><SolidColorBrush x:Key="CustomControlBackground" Color="#EDEDED" /><SolidColorBrush x:Key="CustomControlBorderBrush" Color="#b9bac1" /><!-- Button Style --><LinearGradientBrush x:Key="ButtonBackgroundColorBusrh" EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#fdfdfd" Offset="0"/><GradientStop Color="#f5f5f5" Offset="0.5"/><GradientStop Color="#e9e9e9" Offset="1"/></LinearGradientBrush> </ResourceDictionary>

?? 基礎部分就先簡單的介紹到這里,接下來我們看看WPF的UI開發需要那些技能。

具備技能

??? 1.Blend :要做WPF的界面開發,Blend是必備的技能。通過Blend可以很方便的畫出UI,并且可以通過手動的方式添加動畫效果。Blend的界面和Visual Studio很相似,所以上手也比較快。

??? 2.PhotoShop:不管是做Web前段還是WPF前段,使用PhotoShop切圖以及簡單的設計某些圖片效果的技能也是需要掌握的。并且界面開發人員只掌握到這個程度就可以了。

??? 3.Snoop:是一個捕獲WPF界面層次結構的工具,通過簡單的操作就可以看到我們開發的WPF界面的完整層次結構,并且能夠看到每層元素的屬性值。當界面出現某些問題時,通過Snoop分析界面可達到事半功倍的效果。另外,在通過Visual Studio調試代碼時,通過監視功能也能看到界面的層次結構。

??? 4.素材資源:這里提供一些資源網站Icon Find(http://findicons.com/)、Icon Finder(https://www.iconfinder.com/)。

工程結構

??? UI界面的工程一般包括:Component和Theme兩個工程。自定義控件一般都是添加在Component工程里邊,而控件的樣式模板都會添加到Theme工程里邊。

??? 先看下我們的Component工程,工程名稱為HeaviSoft.FrameworkBase.Component。工程結構如下:

??? 如果我們新增一個WPF的Custom Control,Visual Studio會自動在工程下面創建/Themes/Generic.xaml文件。并且自動在Generic.xaml文件中創建一個簡單的控件模板。代碼如下:

<Style TargetType="{x:Type docking:CustomDocumentPanel}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type docking:CustomDocumentPanel}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><ContentPresenter /></Border></ControlTemplate></Setter.Value></Setter></Style>

??? 上面的模板展示出來就是一片空白的效果,什么都有沒有。所以我們得重寫Template。其實稍后我們在Theme會重寫這些控件的模板,但為什么這里也需要重寫?Component下寫的模板是控件的默認模板。但我們沒有任何主題時,就顯示該默認模板。

??? 只要創建一個控件,Generic.xaml中就會增加一個TargetType為控件類型的模板,如果增加個10個控件,Generic.xaml中就增加了10個Style,代碼量比較龐大。所以,我都會單獨按照控件的名稱在Theme下單獨創建一個同名的資源文件。并把Generic.xaml中的對應樣式移動到這個同名資源文件。而Generic.xaml中值用存放資源引用路徑即可。例如:

<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:HeaviSoft.FrameworkBase.Component" xmlns:docking="clr-namespace:HeaviSoft.FrameworkBase.Component.Docking"><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomWindow.xaml" /><ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTextBox.xaml" /><ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomMessageBox.xaml" /><ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTabControl.xaml" /><ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTabItem.xaml" /></ResourceDictionary.MergedDictionaries> </ResourceDictionary>

??? Component工程結構介紹完了,接下來看Theme工程。我們的Theme工程命名為HeaviSoft.FrameworkBase.Theme。工程結構如下:

??? 工程包含CustomControl和Themes兩個文件夾,CustomControl文件夾下有多個以控件名稱命名的.xaml文件,例如Button.xaml文件,它里邊的代碼就是真正我們自己實現的Button模板。Themes下包含一個GrayWhite文件夾,這個文件夾就是我們的一個主題,它下面存放了各個控件模板需要的資源,包括圖片、顏色、字體等。例如,我們在CustomControl下添加了CustomWindow界面模板,界面右上角需要關閉、最大化、最小化按鈕圖片。這些圖片資源存放在/Themes/GrayWhite/Images/Window下。而CustomWindow需要的顏色以及字體資源分別存放在/Themes/GrayWhite/ColorBrush.xaml和/Themes/GrayWhite/Text.xaml中。同Component相似,/Themes/Generic.xaml的內容就是引用模板、圖片、顏色、字體資源文件。在系統啟動時,只需要動態加載/Themes/Generic.xaml文件就可加載所有的資源文件了。

開發步驟

??? UI控件的開發步驟我們就拿實現自定義Window來舉例。先看看實現的效果:

??? 自定義窗口包含兩個部分,TitleBar和Body。TitleBar從左到右分別包含了圖標、標題、最小化、最大化、關閉按鈕。而Body部分主要包含我們在界面添加的內容。接下來就讓我們一步步的去實現上圖的界面功能。

第一步,Component添加自定義控件

??? 首先,選擇HeaviSoft.FrameworkBase.Component工程,添加Custom Control(WPF)。添加后,CustomWindow的默認代碼為:

public class CustomWindow : Window{static CustomWindow(){DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow), new FrameworkPropertyMetadata(typeof(CustomWindow)));} }

??? CustomWindow類里邊需要添加什么內容我們先不管。Visual Studio在添加CustomWindow文件的同時,會在/Themes/Generic.xaml中添加樣式:

<Style TargetType="{x:Type control:CustomWindow}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type control:CustomWindow}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"></Border></ControlTemplate></Setter.Value></Setter></Style>

?? 上面的樣式其實只是顯示一個空的Window界面,它在什么時候會被用到?當我們沒有引用任何的主題時,默認加載該樣式。

第二步,Theme中添加模板

?? 接下來我們切換到HeaviSoft.FrameworkBase.Theme工程,在CustomControl文件夾下添加資源文件CustomWindow.xaml。然后在/Themes/Generic.xaml中添加CustomWindow.xaml文件的引用。引用的代碼如下:

<ResourceDictionary Source="/HeaviSoft.FrameworkBase.Theme;component/CustomControl/CustomWindow.xaml" />

??? 接下來我們就在CustomWindow.xaml中實現自定義Window模板,這里先給出代碼,然后慢慢分析:

<Style x:Key="WindowStyle" TargetType="{x:Type control:CustomWindow}"><Setter Property="WindowStyle" Value="None" /><Setter Property="AllowsTransparency" Value="True" /><Setter Property="Background" Value="Transparent" /><Setter Property="ResizeMode" Value="NoResize" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type control:CustomWindow}"><Border x:Name="MainBorder" Background="{DynamicResource CustomControlBackground}" CornerRadius="6" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" ><Grid><Grid.RowDefinitions><RowDefinition Height="50"/><RowDefinition Height="1" /><RowDefinition Height="*" /></Grid.RowDefinitions><Border Grid.Row="0" Panel.ZIndex="101" x:Name="PART_TITLEBAR" BorderThickness="0" CornerRadius="6, 6, 0, 0" Background="{DynamicResource Window_TitleBar_Background}"><DockPanel LastChildFill="False" ><Image DockPanel.Dock="Left" Source="{TemplateBinding Icon}" /><Label VerticalAlignment="Center" DockPanel.Dock="Left" Content="{TemplateBinding Title}"Style="{DynamicResource TitleStyle}"/><Button x:Name="PART_CLOSE" Style="{DynamicResource Window_Titlebar_ButtonStyle}" DockPanel.Dock="Right"><Image x:Name="c" Margin="3, 0" Style="{DynamicResource TitleButtonImageStyle}"DockPanel.Dock="Right" Width="24" Height="24" Source="{DynamicResource Window_Button_Close_ImageBrush}" /></Button><Button x:Name="PART_MAXIMIZE_RESTORE" Style="{DynamicResource Window_Titlebar_ButtonStyle}" DockPanel.Dock="Right"><Image Name="MaximizeRestoreImage" Margin="3, 0" Style="{DynamicResource TitleButtonImageStyle}"DockPanel.Dock="Right" Width="24" Height="24" Source="{DynamicResource Window_Button_Max_ImageBrush}" /></Button><Button x:Name="PART_MINIMIZE" Style="{DynamicResource Window_Titlebar_ButtonStyle}" DockPanel.Dock="Right"><Image Margin="3, 0" Style="{DynamicResource TitleButtonImageStyle}"DockPanel.Dock="Right" Width="24" Height="24" Source="{DynamicResource Window_Button_Min_ImageBrush}" RenderTransformOrigin="0.5,0.5" ></Image></Button></DockPanel></Border><Border BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" Grid.Row="1" /><ContentPresenter Grid.Row="2" Margin="8, 5" /></Grid></Border><ControlTemplate.Triggers><DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=WindowState}" Value="Maximized"><Setter TargetName="MaximizeRestoreImage" Property="Source" Value="{DynamicResource Window_Button_Maximum_ImageBrush}" /></DataTrigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>

??? 首先我們不會使用Window默認的TitleBar,所以我們要隱藏掉Window默認的TitleBar,通過設置:WindowStyle為None就可隱藏掉標題欄和邊框。在之前看到的效果圖中,我們看到Window有使用圓角效果,那么必須設置三個屬性值:

<Setter Property="AllowsTransparency" Value="True" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="ResizeMode" Value="NoResize" />

??? AllowsTransparency為True允許窗口透明,Background為Transparent繼承父窗口的背景色(也即設置背景色為透明)。ResizeMode為NoResize禁止手動拖動窗口大小,隱藏虛線邊框。

??? 然后設置Template的Value,一般都是直接創建一個ControlTemplate節點,并指定TargetType為CustomWindow。

<ControlTemplate TargetType="{x:Type control:CustomWindow}">... </ControlTemplate>

??? 接下就該添加模板的具體實現內容,首先我們要設置有圓角的邊框,那么我們可以在Root節點添加一個Border節點,設置背景色Background和邊框寬度BorderThickness、邊框顏色BorderBrush,并設置CornerRadius圓角度。代碼如下:

<Border x:Name="MainBorder" Background="{DynamicResource CustomControlBackground}" CornerRadius="6" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" >... </Border>

??? 我們知道整個界面包含標題和內容,我還會在標題和內容之間添加一條分割線,所以我們可通過一個包含三行的Grid來實現,第零行為標題欄、第一行為一條Bordrer實現的分割線、第二行就直接為我們的內容,可通過ContentPresenter存放這些內容。第一行和第二行的實現如下:

<Grid><Grid.RowDefinitions><RowDefinition Height="50"/><RowDefinition Height="1" /><RowDefinition Height="*" /></Grid.RowDefinitions>...省略標題欄代碼<Border BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" Grid.Row="1" /><ContentPresenter Grid.Row="2" Margin="8, 5" /></Grid>

??? 這里需要說明的是,一般在寫自定義控件時,控件的內容可通過ContentPresenter來顯示。

??? 其實整個界面實現比較復雜的部分是標題欄部分,我們要顯示圖標、標題、最小化、最大化、關閉按鈕。圖標、標題靠左顯示,而最小化、最大化、關閉按鈕靠右顯示。這里我們想起了DockPanel面板,圖標和標題實現如下:???

<DockPanel LastChildFill="False" ><Image DockPanel.Dock="Left" Source="{TemplateBinding Icon}" /><Label VerticalAlignment="Center" DockPanel.Dock="Left" Content="{TemplateBinding Title}"Style="{DynamicResource TitleStyle}"/>... </DockPanel>

??? 顯示的圖標和標題我們可通過TemplateBinding直接綁定Window的圖標和標題。DockPanel右邊主要是三個按鈕,我們這里拿關閉按鈕分析:???

<Button x:Name="PART_CLOSE" Style="{DynamicResource Window_Titlebar_ButtonStyle}" DockPanel.Dock="Right"><Image x:Name="c" Margin="3, 0" Style="{DynamicResource TitleButtonImageStyle}" DockPanel.Dock="Right" Width="24" Height="24" Source="{DynamicResource Window_Button_Close_ImageBrush}" /></Button>

??? 關閉按鈕是一個Button類型元素,它的內容就是顯示一個圖標,我們知道默認的Button按鈕,本身有一些Normal、MouseOver、Press狀態的效果。但這些效果不是我們想要的,所以必須得重寫,上面代碼我們設置Button的Style為Window_Titlebar_ButtonStyle就是重寫設置了Button的樣式,而嵌套的Image樣式也是引用的TitleButtonImageStyle樣式。Window_Titlebar_ButtonStyle的樣式代碼為:

<Style x:Key="Window_Titlebar_ButtonStyle" TargetType="{x:Type ButtonBase}"><Setter Property="BorderThickness" Value="0" /><Setter Property="Background" Value="Transparent" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ButtonBase}"><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderThickness" Value="0" /></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Background" Value="Transparent" /><Setter Property="BorderThickness" Value="0" /></Trigger></ControlTemplate.Triggers><ContentPresenter … /> </ControlTemplate></Setter.Value></Setter></Style>

??? 代碼中,通過觸發器重寫了MouseOver和IsEnabled為False狀態的顯示效果,CustomWindow.xaml模板就分析到這里。

第三步,實現界面邏輯

??? 現在我們只是實現了界面的可視化界面,當我們點擊最小化按鈕時需要把窗口最小化,怎樣實現這樣的功能?我們又不得不回到HeaviSoft.FrameworkBase.Component工程中添加的CustomWindow類,我們可以通過窗口的可視化層次關系找到最小化按鈕,在查找按鈕之前我們必須知道它們的Name,所以一般我們都會在類的頭部聲明模板部件名稱。如下所示:

/// <summary>/// 自定義界面/// </summary>[TemplatePart(Name = "PART_TITLEBAR", Type = typeof(UIElement))][TemplatePart(Name = "PART_CLOSE", Type = typeof(Button))][TemplatePart(Name = "PART_MAXIMIZE_RESTORE", Type = typeof(Button))][TemplatePart(Name = "PART_MINIMIZE", Type = typeof(Button))]public class CustomWindow : Window { }

??? 上面的代碼可以讓我們很直觀的知道最小化按鈕的名稱為PART_MINIMIZE,然后我們聲明一個Button屬性:

/// <summary> /// 最小化按鈕/// </summary> private Button MinimizeButton { get; set; }

?? 接下來在什么時機捕獲顯示的按鈕呢?肯定是必須得等在HeaviSoft.FrameworkBase.Theme工程下添加的CustomWindow.xaml模板被加載完后才能查找,正好Control控件為我們提供了可重寫的方法OnApplyTemplate,我們可以在該方法中去遞歸遍歷查找控件。實現代碼如下:

public override void OnApplyTemplate(){base.OnApplyTemplate();AttachToVisualTree();}/// <summary>/// 附加可視化樹到模板/// </summary>private void AttachToVisualTree(){AttachCloseButton();AttachMinButton();AttachMaximizeRestoreButton();AttachTitleBar();}/// <summary>/// 附加最小化按鈕/// </summary>private void AttachMinButton(){if(MinimizeButton != null){MinimizeButton.Command = null;}var minimizeButton = GetChildControl<Button>("PART_MINIMIZE");if(minimizeButton != null){minimizeButton.Command = MinimizedCommand;MinimizeButton = minimizeButton;}}

??? OnApplyTemplate方法調用了AttachToVisualTree方法,而AttachToVisualTree方法中又調用了AttachMinButton方法,AttachMinButton方法通過GetChildControl<Button>("PART_MINIMIZE")來查找模板中的最小化按鈕,找到之后綁定MinimizedCommand命令,命名的初始化代碼片段如下:

<public CustomWindow() {CreateCommandBindings(); }/// <summary> /// 創建綁定命令 /// </summary> private void CreateCommandBindings() {CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, (a, b) => { Close(); }));CommandBindings.Add(new CommandBinding(MinimizedCommand, (a, b) => { WindowState = WindowState.Minimized; }));CommandBindings.Add(new CommandBinding(MaximizeRestoreCommand, (a, b) => { WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; })); }/// <summary>/// 最小化指令 /// </summary>private readonly RoutedCommand MinimizedCommand = new RoutedUICommand("Minmize", "Minmize", typeof(CustomWindow));/// <summary> /// 最大化復原指令 /// </summary> private readonly RoutedCommand MaximizeRestoreCommand = new RoutedUICommand("MaximizeRestore", "MaximizeRestore", typeof(CustomWindow));

??? 代碼也很簡單,首先聲明一個Command,然后在CreateCommandBindings方法中為聲明的Command綁定命名,比如最小化指令綁定的方法為:(a, b) => { WindowState = WindowState.Minimized; },直接設置WindowState的值為Minimized最小化。其他的操作,例如最大化、關閉以及窗口的拖拽可直接查看源代碼了解,這里就不在做分析了。

第四步,提取主題資源

??? 一個自定義控件的實現我們差不多完成了90%,還剩一個任務就是把模板文件中的主題資源(例如顏色、圖片)提取到主題/Themes/下面去。例如前面實現的Window模板中,最小化、最大化等按鈕有使用圖片,比較好的設計肯定是不會直接使用圖片路徑,所以我們把這些資源提取到/Themes/ColorBrush.xaml資源文件中去。提取資如下:

<SolidColorBrush x:Key="Window_Title_Font_Color" Color="#77818b" /> <SolidColorBrush x:Key="Window_Font_Color" Color="#77818b" /> <SolidColorBrush x:Key="Window_Dark_Font_Color" Color="#fff" /> <BitmapImage x:Key="Window_Button_Close_ImageBrush" UriSource="Images/Window/close.png" /> <BitmapImage x:Key="Window_Button_Min_ImageBrush" UriSource="Images/Window/min.png" /> <BitmapImage x:Key="Window_Button_Max_ImageBrush" UriSource="Images/Window/max.png" /> <BitmapImage x:Key="Window_Button_Maximum_ImageBrush" UriSource="Images/Window/maximum.png" />

??? Window模板中我們只使用這些Key值即可。而不關系圖片的具體路徑。這樣設計,以后我們切換主題時就很方便。到目前為止,怎樣完完整整的添加一個自定義控件就介紹完了。由于代碼量比較大,所以在上面介紹時只貼出了部分代碼,完整的代碼可在GitHub上獲取。

總結

??? 能看到這里的,也辛苦各位了。本片主要介紹了:

??? (1).WPF界面設計的幾個基礎技術點,包括Style、Template、Theme。

??? (2)界面設計的工具和資源,包括Blend、PhotoShop、Snoop以及網站資源。

?? (3)工程結構。

?? (4)自定義控件實現過程。

??? 如果本篇內容對大家有幫助,請點擊頁面右下角的關注。如果覺得不好,也歡迎拍磚。你們的評價就是博主的動力!下篇內容,敬請期待!

源代碼

??? 完整的代碼存放在GitHub上,代碼路徑:https://github.com/heavis/Documentor_V01R01/。

轉載于:https://www.cnblogs.com/w-wanglei/p/5274298.html

總結

以上是生活随笔為你收集整理的准备.Net转前端开发-WPF界面框架那些事,UI快速实现法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。