WPF 入門 (一) XAML 基礎知識


此入門教程是記錄下方參考資料視頻的學習過程
開發工具:Visual Studio 2019

參考資料:https://www.bilibili.com/video/BV1ht411e7Fe

其實也不能算是入門教程,畢竟 WPF 已經出來很久了,教程多的很,我看的教程還挺舊的,就當是我的一個備忘錄吧

目錄

WPF 入門 (一) XAML 基礎知識

WPF 入門 (二) MVVM 入門

WPF 入門 (三) MVVM 提高

剖析最簡單的 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 中為對象屬性賦值

三種方式:

  1. Attribute = Value
  2. 屬性標簽
  3. 標簽擴展

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 中使用可以防止被外部修改,增強安全性

WPF 入門 (一) XAML 基礎知識 結束


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM