【6月日新计划03】WPF入门-XAML詳解

552 阅读8分钟

【6月日新计划03】WPF入门-XAML詳解

WPF .NET Framework | Microsoft Learn

1. WPF Introduction

WPF(Windows Presentation Foundation)是微软推出的基于Windows 的用户界面框架,属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面。

1.1 Develop Env

Tool:Visual Studio 2019 +

Language: C#

UI Framework:WPF

Running Framework:.NET6 / Framework4.5+

2. XAML

XAML是Extensible Application Markup Language的英文缩写,相应的中文名称为可扩展应用程序标记语言,它是微软公司为构建应用程序用户界面而创建的一种新的描述性语言

和xml,html語法都很相似,以 < 開始,以 > 結束。也可以自結束 />.

2.1 特性語法

特性语法是最简化的属性设置语法,并且对曾使用过标记语言的开发人员而言是最直观的语法。

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

2.2 屬性元素語法

于对象元素的某些属性,无法使用特性语法,因为无法在特性语法的引号和字符串限制内充分地表达提供属性值所必需的对象或信息。 对于这些情况,可以使用另一个语法,即属性元素语法。

属性元素开始标记的语法为<TypeName.PropertyName>。

<Button>
  <Button.Background>
    <SolidColorBrush Color="Blue"/>
  </Button.Background>
  <Button.Foreground>
    <SolidColorBrush Color="Red"/>
  </Button.Foreground>
  <Button.Content>
    This is a button
  </Button.Content>
</Button>

2.3 集合語法

如果某个特定属性采用集合类型,则在标记中声明为该属性的值内的子元素的项将成为集合的一部分。

<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>

2.4 Event

2.4.1 點擊事件

通過Click="Button_Click"實現點擊事件

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ExampleNamespace.ExamplePage">
    <Button Click="Button_Click" >Click Me!</Button>
</Page>
namespace ExampleNamespace 
{ 
    public partial class ExamplePage 
    { 
        void Button_Click(object sender, RoutedEventArgs e) 
        { 
            Button b = e.Source as Button; 
            b.Foreground = Brushes.Red; 
        } 
    } 
}

C#中的寫法

 <Button x:Name="Button1" >Click Me!</Button>
 
 
 this.Button1.Click+ =  new RoutedEventHandler(Button_Click)

2.4.2 路由事件

路由事件使一个元素可以处理另一个元素引发的事件,前提是这些元素通过树关系连接在一起。

2.5 标记扩展

1.实际项目中为XAML控件属性赋值经常遇到:设计时属性之处于未知状态 ,运行时才能获取到
2.轻松实现XAML页面属性赋值,资源引用,类型转换等操作

WPF 应用编程中最常用的标记扩展是 Binding (用于数据绑定表达式)以及资源引用 StaticResourceDynamicResource

  • Binding XAML载入时,将数据绑定到XAML对象
  • StaticResource 引用数据字典中定义和静态资源
  • DynamicResource 通过将值推迟为对资源的运行时引用来为属性提供值。
  • RelativeSource 对特定数据源绑定
  • TemplateBinding XAML页面中对象模板绑定调用
<object property="{Binding}" .../>
-or-
<object property="{Binding bindProp1=value1[, bindPropN=valueN]*}" ...
/>
-or-
<object property="{Binding path}" .../>
-or
<object property="{Binding path[, bindPropN=valueN]*}" .../>

2.5.1 Binding

  • BindingGroupName:标识可能的绑定组的字符串。
  • BindsDirectlyToSource:布尔值,可以是 或 true false 。 默认值为 false 。
  • Converter:可以在表达式中设置为字符串,但若要这样做,需要值的对象引用,例如 bindProp = value StaticResource 标记扩展。 在这种情况下,该值是自定义转换器类的实例。
  • ConverterCulture:表达式中的 settable 作为基于标准的标识符;请参阅 的参考主题 ConverterCulture 。
  • ConverterParameter:可以在表达式中设置为字符串,但这取决于要传递 bindProp = value 的参数的类型。 如果传递值的引用类型,则此用法需要对象引用,如嵌套 的 StaticResource 标记扩展。
  • ElementName:互斥与RelativeSourceSource;其中每个绑定属性都表示特定的绑定方法。
  • FallbackValue:可以在表达式中 bindProp = value 设置为字符串,但这取决于要传递的值的类型。 如果传递引用类型, 需要对象引用,如嵌套的StaticResource 标记扩展。
  • IsAsync:布尔值,可以是或 true false 。 默认值为 false 。
  • Mode:value 是枚举中的常量 BindingMode 名称。 例如, {Binding Mode=OneWay} 。
  • NotifyOnSourceUpdated:布尔值,可以是 或 true false 。 默认值为 false 。
  • NotifyOnTargetUpdated:布尔值,可以是 或 true false 。 默认值为 false 。
  • NotifyOnValidationError:布尔值,可以是 或 true false 。 默认值为 false 。
  • Path:一个字符串,用于描述数据对象或常规对象模型的路径。 格式提供了几种不同的约定,用于遍历本主题中无法充分描述的对象模型。 请参阅 PropertyPath XAML 语法。
  • RelativeSource:与和互ElementName 斥 Source;其中每个绑定属性都表示特定的绑定方法。
  • Source:互斥与 RelativeSourceElementName 和 ;其中每个绑定属性都表示特定的绑定方法。
  • StringFormat:一个字符串,描述绑定数据的字符串格式约定。
  • TargetNullValue:可以在表达式中设置为字符串,但这取决于要传递 bindProp = value 的参数的类型。
  • 如果传递值的引用类型, 需要对象引用,如嵌套 的 StaticResource 标记扩展。
  • UpdateSourceTrigger:value 是枚举中的常量 UpdateSourceTrigger 名称。 例如{Binding UpdateSourceTrigger=LostFocus} 。 对于此绑定属性,特定控件可能具有不同的默认值。
  • ValidatesOnDataErrors:布尔值,可以是 或 true false 。 默认值为 false 。
  • ValidatesOnExceptions:布尔值,可以是 或 true false 。 默认值为 false 。
  • XPath:一个字符串,描述 XML 数据源的 XMLDOM 的路径。 请参阅 使用 XMLDataProvider 和 XPath 查询绑定到 XML 数据。

以下是不能使用标记 Binding 扩展/表达式窗体 Binding 设置的 {Binding} 的属性。 UpdateSourceExceptionFilter:此属性需要引用回调实现。 不能在 XAML 语法中引用事件处理程序外的其 他回调/方法。 ValidationRules:属性采用 对象的泛型 ValidationRule 集合。 这可表示为对象元素中的属性元素,但没有 现成的属性分析技术可用于 Binding 表达式 Binding。 XmlNamespaceManager

2.6 類型轉換

特性语法Margin

<Button Margin="10,20,10,30" Content="Click me"/>

Margin改为通过包含 Thickness 对象元素的属性元素语法进行设置

<Button Content="Click me">
  <Button.Margin>
    <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
  </Button.Margin>
</Button>

2.7 x: 前缀

下面列出了最常用的 x: 前缀编程构造:

  • x:Key:为 ResourceDictionary(或其他框架中的类似字典概念)中的每个资源设置唯一的键。 在典型的 WPF 应用标记中的所有 x: 用法中,x:Key 可能占到 90%。

  • x:Class:向为 XAML 页提供代码隐藏的类指定 CLR 命名空间和类名。 必须具有这样一个类才能支持每个 WPF 编程模型的代码隐藏,因此即使没有资源,也几乎总是能看到映射的 x:。

  • x:Name:处理对象元素后,为运行时代码中存在的实例指定运行时对象名称。 通常,经常为 x:Name 使用 WPF 定义的等效属性。 此类属性特定映射到 CLR 后备属性,因此更便于进行应用编程,在应用编程中,经常使用运行时代码从初始化的 XAML 中查找命名元素。 最常见的此类属性是 FrameworkElement.Name。 在特定类型中不支持等效的 WPF 框架级 Name 属性时,仍然可以使用 x:Name。 某些动画方案中会发生这种情况。

  • x:Static:启用一个返回静态值的引用,该静态值不是与 XAML 兼容的属性。

  • x:Type:根据类型名称构造 Type 引用。 用于指定采用 Type(例如 Style.TargetType)的特性,但属性经常具有本机的字符串到 Type 的转换功能,因此使用 x:Type 标记扩展用法是可选的。

3. Layout

  • Canvas:子控件提供其自己的布局。

  • DockPanel:子控件与面板的边缘对齐。就是按鈕等組件可以停靠在邊緣。

  • Grid:子控件由行和列定位。就是把當前窗口分成多個行和列的合集。多個小窗口。

  • StackPanel:子控件垂直或水平堆叠。在Grid每一個小窗口中添加組件,只是一旦超出範圍就要隱藏起來。

  • VirtualizingStackPanel:子控件在水平或垂直的行上虚拟化并排列。

  • WrapPanel:也是在Grid每一個小窗口裡面添加組件,它和StackPanel的區別是,一旦超出範圍,就是自動放到下一行。

3.1 Grid

如果我們想將窗口切成田字格

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="xxxx"/>
    <ColumnDefinition Width="可以設置大小"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana"
Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="0"/>
</Grid>

图片.png

注意:Grid可以無限嵌套,但是Border不可以,當一個大的column or Row需要切分的時候,就用Grid。

3.2 StackPanel/WrapPanel

局部容器,grid切分後的每一個小窗口的佈局

StackPanel超過邊界的部分會隱藏掉

WrapPanel超過邊界的部分會另起一行。

<StackPanel Orientation="Horizontal">

<StackPanel Orientation="Vertical">

3.3 DockPanel

同上,只是他停靠在窗口的邊緣。

<DockPanel LastChildFill="False">
	<Button DockPanel.Dock="Top" Content="Button Top"/>
	<Button DockPanel.Dock="Left"  Content="ButtonLeft"/>
	<Button DockPanel.Dock="Right" Content="Button Right"/>
	<Button DockPanel.Dock="Bottom" Content="Button Bottom"/>
	<Button DockPanel.Dock="Right" Content="Button Center "/>
	<Button Content="Button Center "/>
</DockPanel>

3.4 UniformGrid

在有限的容器內,瓜分剩餘的空間。

4. 控件

  • 按钮: Button 和 RepeatButton。

  • 数据显示:DataGrid、ListView 和 TreeView。

  • 日期显示和选项: Calendar 和 DatePicker。

  • 对话框: OpenFileDialog、 PrintDialog和 SaveFileDialog。

  • 数字墨迹: InkCanvas 和 InkPresenter。

  • 文档: DocumentViewer、 FlowDocumentPageViewer、 FlowDocumentReader、 FlowDocumentScrollViewer和 StickyNoteControl。

  • 输入: TextBox、 RichTextBox和 PasswordBox。

  • 布局: Border、 BulletDecorator、 Canvas、 DockPanel、 Expander、 Grid、 GridView、 GridSplitter、 GroupBox、 Panel、 ResizeGrip、 Separator、 ScrollBar、 ScrollViewer、 StackPanel、 Thumb、 Viewbox、 VirtualizingStackPanel、 Window和 WrapPanel。

  • 媒体: Image、 MediaElement和 SoundPlayerAction。

  • 菜单: ContextMenu、 Menu和 ToolBar。

  • 导航: Frame、 Hyperlink、 Page、 NavigationWindow和 TabControl。

  • 选项: CheckBox、 ComboBox、 ListBox、 RadioButton和 Slider。

  • 用户信息: AccessText、 Label、 Popup、 ProgressBar、 StatusBar、 TextBlock和 ToolTip。

4.1 Handler

快速在後端創建方法。

图片.png

5.數據表格

5.1 入門

    <Grid Margin="10">
        <DataGrid ItemsSource="{Binding UserCollection}"></DataGrid>
    </Grid>
namespace BlankApp1.ViewModels.Config
{
    public class DataGridViewModel : BindableBase
    {
        private ObservableCollection<User> userCollection;

        public ObservableCollection<User> UserCollection
        {
            get { return userCollection; }
            set { userCollection = value; RaisePropertyChanged(); }
        }

        public DataGridViewModel()
        {

            UserCollection = new ObservableCollection<User>();
            CreateUserCollection();
        }

        public void CreateUserCollection()
        {

            UserCollection.Add(new User() { Id = 1, Name = "John Doe", Birthday = new DateTime(1971, 7, 23), ImageUrl = "/Image/DataGrid/brother.jpg" });
            UserCollection.Add(new User() { Id = 2, Name = "Jane Doe", Birthday = new DateTime(1974, 1, 17), ImageUrl = "/Image/DataGrid/checken.jpg" });
            UserCollection.Add(new User() { Id = 3, Name = "Sammy Doe", Birthday = new DateTime(1991, 9, 2), ImageUrl = "/Image/DataGrid/wind.jpg" });
        }
        public class User
        {

            public int Id { get; set; }

            public string Name { get; set; }

            public DateTime Birthday { get; set; }

            public string ImageUrl { get; set; }
        }
    }
}

5.2 手動設定

我们可以手动定义所有列,以实现最大控制:

  • DataGridTextColumn
  • DataGridCheckBoxColumn
  • DataGridComboBoxColumn
  • DataGridHyperlinkColumn
  • DataGridTemplateColumn

5.2.1 簡單設定

图片.png

        <Grid Grid.Row="2">
            <DataGrid x:Name="grid" AutoGenerateColumns="False" CanUserAddRows="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding No}" Header="No"/>
                    <DataGridTemplateColumn Header="Color">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Border Height="10" Width="10" Background="{Binding Code}"></Border>
                                    <TextBlock Margin="10,0" Text="{Binding Name}"/>
                                </StackPanel>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="Operate">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <WrapPanel Orientation="Horizontal">
                                    <Button Content="Update"/>
                                    <Button Content="Delete"/>
                                    <Button Content="Save"/>
                                </WrapPanel>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>

5.2.2 有控件

    <Grid Margin="10">
            <DataGrid ItemsSource="{Binding UserCollection}" AutoGenerateColumns="False">
                    <DataGrid.Columns>

                            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />

                            <DataGridTemplateColumn Header="Birthday">
                                    <DataGridTemplateColumn.CellTemplate>
                                            <DataTemplate>
                                                    <DatePicker SelectedDate="{Binding Birthday}" BorderThickness="0" />
                                            </DataTemplate>
                                    </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>

                    </DataGrid.Columns>
            </DataGrid>
    </Grid>

5.2.3 點擊下面顯示詳情

	<Grid Margin="10">
		<DataGrid ItemsSource="{Binding UserCollection}" AutoGenerateColumns="False">
			<DataGrid.Columns>
				<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
				<DataGridTextColumn Header="Birthday" Binding="{Binding Birthday}" />
			</DataGrid.Columns>
			<DataGrid.RowDetailsTemplate>
				<DataTemplate>
					<TextBlock Text="{Binding Details}" Margin="10" />
				</DataTemplate>
			</DataGrid.RowDetailsTemplate>
		</DataGrid>
	</Grid>
    <Grid Margin="15">
        <DataGrid
            Grid.Row="1"
            AutoGenerateColumns="False"
            ItemsSource="{Binding UserCollection}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
                <DataGridTextColumn Binding="{Binding Birthday}" Header="Birthday" />
            </DataGrid.Columns>
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <DockPanel Background="GhostWhite">
                        <Image
                            Height="64"
                            Margin="10"
                            DockPanel.Dock="Left"
                            Source="{Binding ImageUrl}" />
                        <Grid Margin="0,10">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>

                            <TextBlock FontWeight="Bold" Text="ID: " />
                            <TextBlock Grid.Column="1" Text="{Binding Id}" />
                            <TextBlock
                                Grid.Row="1"
                                FontWeight="Bold"
                                Text="Name: " />
                            <TextBlock
                                Grid.Row="1"
                                Grid.Column="1"
                                Text="{Binding Name}" />
                            <TextBlock
                                Grid.Row="2"
                                FontWeight="Bold"
                                Text="Birthday: " />
                            <TextBlock
                                Grid.Row="2"
                                Grid.Column="1"
                                Text="{Binding Birthday, StringFormat=d}" />

                        </Grid>
                    </DockPanel>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
    </Grid>