此入門教程是記錄下方參考資料視頻的學習過程
開發工具:Visual Studio 2019
其實也不能算是入門教程,畢竟 WPF 已經出來很久了,教程多的很,我看的教程還挺舊的,就當是我的一個備忘錄吧
目錄
剖析最簡單的 WPF 程序
隨便創建一個 WPF 項目
App.xaml
App.xaml 的 Application 標簽中的 StartUri="MainWindow.xaml"
,就是把 MainWindow 當作主窗體
MainWindow.xaml
xaml 是一種聲明性語言,xaml 編譯器會為每個標簽聲明一個對應標簽的對象
標簽分為開始標簽和結束標簽,開始標簽里可以寫一些屬性 Attribute
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:xaml namespace 的縮寫
http://schemas.microsoft.com/winfx/2006/xaml/presentation
:不是網址,是一個硬編碼的字符串,告訴 xaml 編譯器,就把這一系列類庫引入到當前的 xaml 文件里
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:x:x 是命名空間映射的名稱,xaml 規定可以有一個命名空間是不加名稱的,就是默認命名空間,映射命名空間之后以
名稱:標簽
或名稱:屬性
方式使用 x:Class
:就是這個類的命名空間,用於指示 xaml 文件與哪個 C# 編譯的類合並到一起
MainWindow
MainWindow 這個類是 partial 聲明的,所以它可以出現在多個地方,MainWindow.xaml 和 MainWindow.xaml.cs 編譯完會合並到一起
InitializeComponent()
就是由 xaml 生成的
啟動時的窗口是由 App.xaml 的 StartupUri 屬性決定的,指向的是 xaml 文件,不是命名空間
可以使用反編譯工具查看
淺析用戶界面的樹形結構
傳統方法繪制 UI 和 WinForm 是平面布局的界面,沒有包含
WPF 是樹形結構,可以嵌套,可以在 Blend for Visual Studio 中打開項目查看
比如這么一個界面,代碼會在之后的隨筆里給出
在 XAML 中為對象屬性賦值
三種方式:
- Attribute = Value
- 屬性標簽
- 標簽擴展
Attribute = Value
Human 類,在 MainWindow 中
public class Human
{
public string Name {get;set;}
public Human Child {get;set;}
}
用標簽形式聲明一個 Human 對象
注冊命名空間,當前程序集的命名空間一般使用 local
xmlns:local="clr-namespace:命名空間"
在 Window.Resources
標簽中聲明 Human 對象
<Window.Resources>
<local:Human x:Key="human" Name="ZhangSan">
</Window.Resources>
每一個 WPF 程序以一個資源字典的形式維護一系列資源, x:Key="human"
就是一個鍵
檢索資源
Human h = this.FindResource("human") as Human;
if(null != h)
{
MessageBox.Show(h.Name);
}
字符串值轉化為其它類型
如何智能的轉化類型?
將 Child 的值轉化為對象
<Window.Resources>
<local:Human x:Key="human" Name="ZhangSan" Child="LiSi">
</Window.Resources>
添加一個 TypeConverter 類
public class NameToHumanTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
string name = value.ToString();
Human child = new Human();
child.Name = name;
return child;
}
}
ConvertFrom()
中的 value 參數就是在 xaml 中使用 Attribute = "value" 這種形式賦值的時候我們得到的 value
[TypeConverterAttribute(typeof(NameToHumanTypeConverter))]
public class Human
{
public string Name {get;set;}
public Human Child {get;set;}
}
缺點:
- 只能使用簡單的字符串進行賦值,無法應付復雜的類型轉換,比如,在按鈕的 Content 屬性里加入一個 Rectangle 標簽
屬性標簽
<Button Width="120" Height="30" />
使用 />
結尾,是一個空標簽,就是說這個標簽不具有內容
在開始標簽和結束標簽之間的就是 內容 Content ,是標簽的內容
<Button Width="120" Height="30" >
<!--Content-->
<Button.Content>
<Rectangle Width="20" Height="20" Stroke="DarkGreen" Fill="LawnGreen" />
</Button.Content>
</Button>
效果
屬性標簽就是 類名.屬性
,Button.Content
中的 Button 不能省略,否則 xaml 編譯器會認為你想要聲明一個對象
可以使用復雜對象,彌補了 Attribute = Value
的缺點
例子
畫一個單一顏色的矩形框
<Rectangle Width="200" Height="200" Stroke="Blue" Fill="LightBlue" />
效果
若想畫一個漸變顏色的矩形框,就需要使用復雜對象
<Rectangle Width="200" Height="200">
<Rectangle.Fill>
<LinearGradientBrush>
<LinearGradientBrush.StartPoint>
<Point X="0" Y="0" />
</LinearGradientBrush.StartPoint>
<LinearGradientBrush.EndPoint>
<Point X="1" Y="1" />
</LinearGradientBrush.EndPoint>
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0.2" Color="LightBlue" />
<GradientStop Offset="0.7" Color="DarkBlue" />
<GradientStop Offset="1.0" Color="Blue" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
效果
缺點:
- 代碼變得很復雜
建議:
- 能使用 Attribute = Value 賦值的地方,一定使用 Attribute = Value 的形式賦值
- 如果需要的屬性值正好是默認值,就不要寫出來
- 標簽集合可以把元素寫在內容區域
優化之后的代碼
<Rectangle Width="200" Height="200">
<Rectangle.Fill>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.2" Color="LightBlue" />
<GradientStop Offset="0.7" Color="DarkBlue" />
<GradientStop Offset="1.0" Color="Blue" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
標簽擴展
標簽擴展與 Attribute = Value 非常類似,使用 {}
例子,添加命名空間 xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Window.Resources>
<sys:String x:Key="stringHello">Hello WPF!</sys:String>
</Window.Resources>
<Grid>
<TextBlock Height="24" Width="120" Background="LightBlue" Text="{StaticResource ResourceKey=stringHello}"/>
</Grid>
ResourceKey 可以省略,直接使用 stringHello ,構造函數的參數可以不寫,但是一定要按順序,為了保證代碼的可讀性,寫出來比較好
因為標簽擴展已經使用了雙引號把括號引起來了,所以不需要再把里面是屬性加雙引號,屬性之間使用括號分開
Binding 例子
一個 TextBox 顯示 Slider 的位置
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="4"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<TextBox x:Name="tb" Text="{Binding ElementName=sld,Path=Value}"/>
<Slider x:Name="sld" Grid.Row="2" Value="50" Maximum="100" Minimum="0"/>
</Grid>
事件處理器與代碼后置
<Grid>
<Button x:Name="button1" Width="200" Height="200" Content="Click Me" Click="button1_Click"/>
</Grid>
Click 就是一個事件,Click="button1_Click"
就是事件訂閱
可以手寫事件處理器,也可以自動生成
自動創建的事件會以 事件擁有者 + 事件 ,中間以 下划線 隔開命名,
以 C# 代碼綁定事件
public MainWindow()
{
InitializeComponent();
//兩種方法綁定事件
//方法一
this.button1.Click += this.button1_Click;
//方法二
this.button1.Click += new RoutedEventHandler(this.button1_Click);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
}
導入程序集和引用其中的名稱空間
模塊化引用 WPF 窗口
使用 User Control Library
新建一個 空白解決方案
新建一個 WPF 項目和一個 User Control Library 項目
User Control Library 項目的命名為 ControlLibrary
User Control Library 項目刪除自帶的 xaml 文件,新建一個 User Control,命名 SalaryCalculator
SalaryCalculator.xaml 代碼
<UserControl x:Class="ControlLibrary.SalaryCalculator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ControlLibrary"
Width="240" Height="160" Background="LightBlue">
<Canvas>
<Label Name="label1" Content="基本工資" Canvas.Left="12" Canvas.Top="12" Height="28"/>
<Label Name="label2" Content="崗位工資" Canvas.Left="12" Canvas.Top="46" Height="28"/>
<Label Name="label3" Content="實際工資" Canvas.Left="12" Canvas.Top="80" Height="28"/>
<TextBox Name="textBox1" Canvas.Left="88" Canvas.Top="14" Height="23" Width="140"/>
<TextBox Name="textBox2" Canvas.Left="88" Canvas.Top="48" Height="23" Width="140"/>
<TextBox Name="textBox3" Canvas.Left="88" Canvas.Top="82" Height="23" Width="140"/>
<Button Name="button1" Content="計算" Canvas.Left="88" Canvas.Top="125" Height="23" Width="140" Click="button1_Click"/>
</Canvas>
</UserControl>
事件函數,寫的隨便一些,結果不是這次不是重點
private void button1_Click(object sender, RoutedEventArgs e)
{
this.textBox3.Text = this.textBox1.Text + this.textBox2.Text;
}
編譯一下
如何應用到主界面上
WPF 項目
右鍵依賴項,選擇添加項目引用,這樣在 xaml 中寫命名空間就有智能提示了
xmlns:controls="clr-namespace:ControlLibrary;assembly=ControlLibrary"
完整的窗體代碼
<Window x:Class="導入程序集和引用其中的名稱空間.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:導入程序集和引用其中的名稱空間"
xmlns:controls="clr-namespace:ControlLibrary;assembly=ControlLibrary"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<controls:SalaryCalculator Grid.Column="0" Grid.Row="0"/>
<controls:SalaryCalculator Grid.Column="1" Grid.Row="0"/>
<controls:SalaryCalculator Grid.Column="0" Grid.Row="1"/>
<controls:SalaryCalculator Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
效果
Telerik 可以買控件
XAML 的注釋
<!-- -->
Visual Studio 快捷鍵
注釋:Ctrl + K Ctrl + C
解注釋:Ctrl + K Ctrl + U
x 名稱空間及其常用元素
x 名稱空間的由來和作用
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
是對 x 名稱空間的聲明
x 名稱空間里的類都是用來解析和分析 xaml 代碼的,即 x 名稱空間就是 xaml 名稱空間
x 名稱空間里都有些什么
標簽擴展
- x:Array
- x:Null
- x:Static
- x:Type
Attribute
- x:Class
- x:ClassModifier
- x:FieldModifier
- x:Key
- x:Name
- x:Shared
- x:Subclass
- x:TypeArguments
- x:Uid
XAML 指令元素
- x:Code
- x:XData
x:Class
x:Class 是用來聲明類的
指示 xaml 文件所聲明的這個類,最后要與哪個名稱空間中的哪個類進行合並
例如 x:Class="WpfApplication1.MainWindow"
就是 WpfApplication1 名稱空間下的 MainWindow 類與 xaml 合並
x:ClassModifier
x:ClassModifier 指的是類的訪問級別
xaml 文件中的取值必須和類的訪問權限一致,否則會報錯
x:Name
是 x 名稱空間中的 Name ,不是 Name 屬性
作用
- 為 xaml 生成的實例創建引用變量
- 若實例有 Name 屬性,同時用變量的這個名字給 Name 屬性賦值
xaml 語言是一種聲明性語言,每見到一個標簽,就會聲明一個實例,並不是創建一個變量(實例可以獨立存在,變量引用實例)
建議使用 x:Name 而不使用 Name ,因為沒有繼承 FrameworkElement 的類可能沒有 Name ,只能使用 x:Name
x:FieldModifier
控制類中字段的訪問級別,但是是給標簽用的
在 User Control Library 中使用可以防止被外部修改,增強安全性