赞
踩
WPF(Windows Presentation Foundation)提供了多种布局容器来帮助开发者设计用户界面,以下是一些常用的布局:
WPF中的Grid是一个功能强大的布局系统,它允许开发者以表格的形式对UI元素进行精确的定位和布局。
在WPF中,Grid控件由行和列组成,这些行和列的集合分别称为行集合和列集合。开发者可以通过定义行(RowDefinitions)和列(ColumnDefinitions)的数量、大小以及其他属性来创建复杂的布局结构。以下是Grid的一些关键特性:
RowDefinitions
和ColumnDefinitions
属性来定义Grid的行数和列数。每个RowDefinition
或ColumnDefinition
对象可以设置高度或宽度,以及是否强制其内容适应可用空间。Grid.Row
和Grid.Column
附加属性来指定,这些属性的值是从0开始的索引数。例如,Grid.Row="1"
和Grid.Column="2"
将元素放置在第二行第三列的位置。HorizontalAlignment
)和垂直对齐(VerticalAlignment
),以便灵活控制子元素在单元格中的位置。Grid.ShowGridLines
属性为True
来显示网格线,这有助于在设计时更清晰地观察和调整布局。<Grid> <!--默认情况下StackPanel容器垂直排列内部元素,可通过Orientation="Horizontal"设置为水平排列--> <StackPanel Orientation="Horizontal"> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> </StackPanel> <!--默认情况是水平排列,但是当剩余空间不足的时候会自动换行--> <WrapPanel> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> <Button Height="40" Width="80"/> </WrapPanel> <!--Grid类似于table表格,可灵活设置行列并放置控件元素--> <!--RowDefinitions水平划分--> <Grid.RowDefinitions> <RowDefinition Height="auto"/><!--设置高度自适应,根据其内部的元素,如上面设置的按钮总共站有的高度来设置--> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="2*"/><!--倍数设置:第二列是第一列的两倍--> </Grid.ColumnDefinitions> <!--填充第二行第二列--> <Border Background="Red" Grid.Row="1" Grid.Column="1"/> </Grid>
介绍:在WPF中,样式是一种可以重复使用的视觉元素集合,它用于定义控件的外观和行为。以下是WPF中样式的一些详细说明:
<1>对于样式资源,可以单独定义在每个窗口中,中,但是只能被此窗口的控件对象所调用
<Window.Resources>
<!-- Key:通过Key来调用样式;TargetType="Button"确定样式的类型,只能给Button用-->
<Style x:Key="Button_Sytle1" TargetType="Button">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="30"/>
</Style>
</Window.Resources>
<2>如果想要所有窗口的对象均可调用,需要创建-》资源字典ResourceDictionary,并在App.xaml中进行声明
(1)概念:当我们不想使用某个控件他的系统预设外观,想要自定义控件的外观和样式时,就需要编写控件的ControlTemplate。WPF 的 ControlTemplate 是一种用于定义和自定义控件的外观和结构的模板,它可以完全替换控件的默认模板,实现个性化和复杂的效果。
(2)WPF 的 ControlTemplate 有以下几个特点:
(3)想要重塑一个控件,我们得先知道原来的控件是如何构成编写的。例如对于一个按钮,我们可以创建一个WPF项目, 创建一个Button按钮, 然后选中该按钮, 右键选择编辑模板>编辑副本。
创建完成后, 会在当前页面<Windows.Resources> 键下面生成一些样式片段 , 一个key为ButtonStyle1的样式。
通过这一段代码,我们可以知道,Button的模板Template是由一个底板Border和中心内容呈现控件ContentPresenter组成的。因此,我们在编写按钮的自定义控件模板时,就可以对这两个组成元素进行自定义属性设定。
<1>属性(Property)触发器
<Style x:Key="Button_Sytle1" TargetType="Button" > <Setter Property="Width" Value="90"/> <Setter Property="Height" Value="30"/> <!--1.单条件触发器--> <Style.Triggers> <!--Property:设置触发器监测的对象。鼠标是否进入,Value:True表示为True时表示进行--> <Trigger Property="IsMouseOver" Value="True"> <!--当鼠标进入按钮时字体颜色变成蓝色--> <Setter Property="Foreground" Value="Blue"/> <Setter Property="FontSize" Value="20"/> </Trigger> <Trigger Property="IsMouseOver" Value="False"> <!--当鼠标离开按钮时字体颜色变成红色--> <Setter Property="Foreground" Value="Red"/> <Setter Property="FontSize" Value="15"/> </Trigger> </Style.Triggers> </Style>
<Style x:Key="Button_Sytle2" TargetType="Button" > <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="50"/> <!--1.多条件触发器--> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <!--Conditions集合用来设置多个触发条件--> <Condition Property="IsMouseOver" Value="True"/> <Condition Property="IsFocused" Value="True"/> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Foreground" Value="Blue"/> <Setter Property="FontSize" Value="20"/> </MultiTrigger.Setters> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <!--Conditions集合用来设置多个触发条件--> <Condition Property="IsMouseOver" Value="False"/> <Condition Property="IsFocused" Value="False"/> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="FontSize" Value="18"/> <Setter Property="Foreground" Value="Red"/> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers> </Style>
<Window.Resources> <Style x:Key="Button_Style" TargetType="Button"> <Setter Property="Background" Value="LightBlue"/> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="40"/> <!--行为触发器,我们为一个按钮定义了一个样式,当按钮被点击时,它的背景颜色会在2秒内从浅蓝色变为红色--> <Style.Triggers> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetProperty="Background.Color" To="Red" Duration="0:0:2"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <StackPanel> <Button Style="{StaticResource Button_Style}" Content="行为触发器"/> </StackPanel> </Grid>
中文译本将Data Binding译为了数据绑定,很大程度上其实是拼音音译,没有实际意义。WPF中的Binding更多地是表达一种桥梁关系。Binding对象的两端,分别是源(Source)和目标(Target),源即数据来源,目标就是数据要到达的地方。一般情况下,源一般是逻辑层的对象,目标是UI层的控件对象,数据由源通过Binding对象送达UI层,也就完成的数据驱动UI的过程。
如图所示,数据绑定实质上是绑定目标与绑定源之间的桥梁。 该图演示了以下基本的 WPF 数据绑定概念:
通常情况下,每个绑定具有四个组件:
例如,如果将 TextBox
的内容绑定到 Employee.Name
属性,则可以类似如下所示设置绑定:
Employee
Name
结合图像我们可以这样理解:数据来源是Employee类的Name属性,数据要去往的目标是TextBox的Text属性上,所以我们将TextBox的Text属性绑定到Employee类的Name属性上,当我们从TextBox经过Binding这座桥梁抵达源,也就是Employee类时,再通过一段路径走到Name属性上。接下来我们来展开这个例子。
数据源是一个对象,一个对象上有很多数据,外界通过属性进行访问,这个属性,就是要设置的路径(Path),也就是要通过Binding送往UI元素的,或者说是UI元素所关心的哪个属性值的变化。Binding要想实现数据流通,就需要让这个属性具备通知Binding属性值发生变化的能力。解决方案就是在属性的set块中激发一个PropertyChanged事件。要想激发这个事件,就需要让数据源的类实现System.ComponentModel命名空间中的INotifyPropertyChanged接口。这样一来,当我们绑定到这个数据源上时,Binding对象就会自动侦听来自这个接口的PropertyChanged事件。
public class Employee:INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; string name; public string Name { get { return name; } set { name = value; if(this.PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name")); } } }
当类实现了INotifyPropertyChanged接口后,会自动声明PropertyChanged事件。在set块下先判断当前事件是否被订阅,若订阅了,则当属性值发生变化时,就会触发事件。PropertyChanged 是一个委托,它是 INotifyPropertyChanged 接口的一个事件,用于通知绑定对象属性的更改。
传入PropertyChanged 的两个参数一是事件源,在这里就是当前这个事件;二是属性变更事件参数,也就是是哪个属性要发生改变需要被监听,把名字报上来。
当你在 XAML 中绑定了一个实现了 INotifyPropertyChanged 接口的类的属性时,PropertyChanged 事件就会被自动订阅,这样当属性值发生变化时,就会触发 PropertyChanged 事件,从而更新 UI 上的绑定值。所以我们需要判断 PropertyChanged 是否为空,是为了避免在没有订阅事件的情况下调用它,否则会引发异常。
PropertyChanged 既是委托,也是事件。委托是一种类型,用于定义方法的签名,可以将方法作为参数传递或赋值给变量。事件是一种特殊的委托,用于在某些情况发生时通知其他对象。事件通常是事件发送方的成员,例如 Click 事件是 Button 类的成员, PropertyChanged 事件是实现 INotifyPropertyChanged 接口的类的成员。
对应的属性的类能够实现通知后,我们在窗体的后台代码去实例化并使用这个类的对象。
public partial class MainWindow : Window
{
Employee employee = new Employee() { Name = "Default Name" };
public MainWindow()
{
InitializeComponent();
}
}
拥有了源,以及UI元素作为目标后,我们要使用Binding对象连通源和目标。
public partial class MainWindow : Window
{
Employee employee = new Employee() { Name = "Default Name" };
public MainWindow()
{
InitializeComponent();
Binding binding = new Binding();
binding.Source = employee;
binding.Path = new PropertyPath("Name");
BindingOperations.SetBinding(this.InputTextBox, TextBox.TextProperty, binding);
}
}
在这里,employee作为数据源,InitializeComponent()是用于初始化UI元素的代码。先实例化Binding对象,并设置源和路径,最后使用BindingOperations.SetBinding()实现数据源与目标链接,其三个参数为:
例如对于下面这个程序,我们额外设置了一个按钮用于输出当前Employee对象的Name属性值。
<Window x:Class="Sketch7.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:Sketch7"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<TextBox x:Name="InputTextBox" Margin="10" FontSize="16" FontFamily="Arial" />
<Button Width="80" Margin="10" FontSize="16" FontFamily="Arial" Content="Output" Click="OutputName_Click"/>
<TextBox x:Name="OutputTextBox" Margin="10" FontSize="16" FontFamily="Arial" Text=""/>
</StackPanel>
</Window>
public partial class MainWindow : Window { Employee employee = new Employee() { Name = "Default Name" }; public MainWindow() { InitializeComponent(); Binding binding = new Binding(); binding.Source = employee; binding.Path = new PropertyPath("Name"); BindingOperations.SetBinding(this.InputTextBox, TextBox.TextProperty, binding); } private void OutputName_Click(object sender, RoutedEventArgs e) { this.OutputTextBox.Text = "My name is"+employee.Name; } }
运行程序,我们发现,刚开始会展示设置的“Default Name”,若改变文本块的内容,则单击output按钮后,其对应的文本也会被改变,说明此时已经实现了双向数据绑定。当然了,并非所有的数据绑定都需要在后置代码中编写来实现,我们也可以在XAML页面使用标记扩展的功能来声明Binding对象实现数据绑定,同样也是需要指定源和路径的,我们稍后会在多种源和路径的形式中进行讨论。
总结一下,在实现数据绑定时,我们需要注意下面的要点:
如果对于上面这个例子,我们希望用户在文本框的输入不会改变到Name的属性值,只有属性值的改变会变更文本框的展示,就需要对数据流的方向进行设置。我们有时希望应用允许用户更改数据,然后将该数据传播回源对象。有时不希望允许用户更新源数据。可以通过设置 Binding.Mode 来控制数据流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。