当WPF应用程序创建好后,系统会自动添加一个Grid控件到窗体上,通过Grid控件能够方便地对界面进行布局.下面代码中为Grid控件添加了两行两列,分别用RowDefinitions属性ColumnDefinitions属性表示行的集合和列的集合,集合中有RowDefinition成员和ColumnDefinition成员分别表示每一行和每一列的元素。Height和Width的值有三种表示形式:
(1)绝对大小:例如Height="100",表示行的绝对高度为100。窗体大小的改变不影响该行的高度变化。
(2)比例大小:例如Height="100*",表示行的相对高度为100,如果另一行的高度为"200*",Grid的高度为600,那么这两行的高度分别为200和400。
(3)自动大小:例如Height="auto",表示该行的高度根据内容自动调整。
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
Grid.RowDefinitions
- 这里定义了两个行 (
RowDefinition
)。 - 第一个行 (
<RowDefinition Height="2*" />
) 的高度被设置为"2*"
,这意味着它将占用两倍的可用垂直空间,相比于其他使用星号 (*) 定义的行。 - 第二个行 (
<RowDefinition Height="1*" />
) 的高度被设置为"1*"
,它将占用剩余的一倍可用垂直空间。
Grid.ColumnDefinitions
- 这里定义了两个列 (
ColumnDefinition
)。 - 第一个列 (
<ColumnDefinition Width="1*" />
) 的宽度被设置为"1*"
,即它将占用一倍的可用水平空间。 - 第二个列 (
<ColumnDefinition Width="2*" />
) 的宽度被设置为"2*"
,即它将占用两倍的可用水平空间。
示例布局
假设 Grid
控件的可用高度是 300 单位,可用宽度是 300 单位(这仅仅是为了示例,实际值取决于 Grid
控件的布局上下文):
- 第一个行的高度将是 200 单位(因为 2/3 * 300 = 200)。
- 第二个行的高度将是 100 单位(因为 1/3 * 300 = 100)。
- 第一个列的宽度将是 100 单位(因为 1/3 * 300 = 100)。
- 第二个列的宽度将是 200 单位(因为 2/3 * 300 = 200)。
<Button Content="红色" Background="Lime" Name="button1" Click="button1_Click" Margin="100"/>
该元素表示在WPF窗体中添加一个Button按钮,并通过设置元素的属性来设置按钮的属性,此处要注意XAML中元素的属性Attribute和类成员的属性Property之间的区分。XAML语句Button元素中的Content属性对应Button成员的Content属性,表示按钮上显示的内容,同样的对应关系,Background属性表示Button的背景颜色,Name属性表示Button的类型变量名,Click表示Button的单击事件,Margin则表示Button显示区域与其所在窗口的边距。当在XAML编辑器中输入Click的时候,按下键盘上的Table键,XAML编辑器上会自动生成代码Click=" ",并在旁边显示一个提示按钮,显示"新建事件处理程序",双击该按钮之后,系统会自动在MainWindow.xaml.cs文件中生成事件处理函数。
从元素的实现代码中可以看出,元素属性赋值使用的都是字符串,对于一个元素的某些属性,如果不能用字符串来表示,则必须用到属性元素语法。属性元素语法即用属性元素本身为该属性赋值,属性语法结构为<类型.属性>,Button类的另一种表示方法如下:
<Button Name="button1" Click="button1_Click">
<Button.Content>红色</Button.Content>
<Button.Margin>100</Button.Margin>
<Button.Background>
<SolidColorBrush Color="Lime"/>
</Button.Background>
</Button>
标记扩展主要为了属性赋值,当为元素属性Attribute赋值时,使用左右花括号来将标记扩展与XAML处理器区分开,本实例代码中为TextBlock的Text属性赋值时,就使用了如下标记扩展:
Text="{StaticResource ResourceKey=name2}" 其中,StaticResource ResourceKey=name2就是标记扩展,这里是属性特定于WPF的标记扩展 。StaticResource主要用于将已定义的资源赋值给XAML属性;DynamicResource用于将定义的资源推迟赋值给XAML属性值。 用于数据绑定的标记扩展Binding.
当WPF应用程序创建完成后,打开MainWindow.xaml文件,在XAML编辑器中就可以发现两个xmlns声明如下:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
其中,第一个声明表示将整个WPF命名空间映射为默认的命名空间,第二个声明则表示将XAML命名空间映射为x:前缀,其中x:前缀属于标记扩展。x:前缀有如下几个常用的标记。
(1)x:Key:为每个资源设置一个唯一标识,例如代码 x:Key="name1"。设置此标记后,就可以将TextBlock控件的Text属性与该标识对应的String关联上。
(2)x:Name:为运行时实例指定变量名称。例如代码 x:Name="name1",在MainWindow类中就可以通过name1作为变量名来访问XAML中定义的TextBlock对象了。
(3)x:Class:为XAML代码中提供CLR命名空间和类名。例如,XAML代码:
x:Class="WpfStringResources.MainWindow"
在一个WPF项目中,XAML被编写在.xaml后缀的文件中,而C#编写的代码则编写在后缀为.xaml.cs文件中,该文件为代码隐藏文件。编译器在编译XAML文件时,XAML代码会通过以"命名空间.类名"的形式为根元素的x:Class属性赋值,指定隐藏文件的位置。通过这种方法将XAML代码与C#代码关联起来。
(4)x:Type:将模板或类型指定在某种类型上。
(5)x:Static:在XAML中获取类型中定义的静态变量。
(6)x:ArrayExtension:用于在XAML中创建数组。
(7)x:Null:设置某个属性值为空。
xmlns:system="clr-namespace:System;assembly=mscorlib"
此代码表示将System命名空间和mscorlib程序集映射到"system:",其中"clr-namespace:"标记表示要映射的CLR命名空间,而"assembly="是指包含CLR命名空间的程序集名称。
XML
<Window x:Class="WpfTextBinding.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!--在设置Text属性的引号内使用左右花括号将扩展标记括起来-->
<!--设置Binding的ElementName属性为需要绑定的控件-->
<!--设置Path属性为需要绑定的控件属性名称-->
<!--设置Mode属性为绑定的方式-->
<TextBlock Text="Text1" FontSize="20" />
<TextBox Name="textBox1" Margin="60" FontSize="20" Text="{Binding ElementName = textBox2, Path = Text, Mode=OneWay}"/>
<TextBlock Text="Text2" FontSize="20" Grid.Column="1" />
<TextBox Name="textBox2" Margin="60" FontSize="20" Grid.Column="1" Text="{Binding ElementName = textBox3, Path = Text, Mode=TwoWay}"/>
<TextBlock Text="Text3" FontSize="20" Grid.Row="1" />
<TextBox Name="textBox3" Margin="60" FontSize="20" Grid.Row="1" Text="{Binding ElementName = textBox4, Path = Text, Mode=OneWayToSource}"/>
<TextBlock Text="Text4" FontSize="20" Grid.Row="1" Grid.Column="1" />
<TextBox Name="textBox4" Margin="60" FontSize="20" Grid.Row="1" Grid.Column="1" Text="{Binding ElementName = textBox1, Path = Text, Mode=OneTime}"/>
</Grid>
</Window>
<!--本实例代码主要使用WPF扩展标记Binding实现了控件的数据绑定功能。也实现了TextBox控件的Text程序的互相绑定,在设置Text属性的引号内使用左右花括号将扩展标记括起来,
设置Binding的ElementName属性为需要绑定的控件,设置Path属性为需要绑定的控件属性名称,设置Mode属性为绑定的方式。Mode有四种常用的绑定方式。
(1)OneWay:表示源控件影响目标控件。正如实例中的Text1的Text属性绑定到Text2的Text属性上,那么Text1为目标控件,Text2为源控件,Text2的Text属性发生改变会影响Text1的Text属性同步发生改变。
(2)TwoWay:表示源控件和目标控件互相影响。如Text2与Text3之间的互相影响。
(3)OneWaySource:表示目标控件影响源控件。如Text3影响Text4。
(4)OneTime:表示只绑定一次。-->
<!--在 WPF (Windows Presentation Foundation) 中,OneWaySource 不是一个标准的绑定模式,但你可能是在提到或误解了 OneWay
或其他与数据绑定相关的概念。在 WPF 中,数据绑定的主要模式有四种:
OneWay: 当源属性更改时,目标属性会更新,但更改目标属性不会影响源属性。
TwoWay: 当源属性或目标属性更改时,另一方都会更新。
OneTime: 当绑定创建时,目标属性会从源属性获取值,但之后不会有进一步的更新。
OneWayToSource: 当目标属性更改时,源属性会更新,但更改源属性不会影响目标属性。-->
代码实现四个文本控件互相绑定文本属性。当在第一个文本控件中输入文本时,其他文本控件不发生改变;当在第二个文本控件中输入文本时,第一个文本控件的文本显示与第二个同步更新;当第三个文本控件获取输入焦点时,第三个文本控件中的文本马上更新,如果在第三个文本控件中输入文本时,第一个和第二个文本控件都发生同步更新;当第四个文本控件获取输入焦点时,第四个文本控件中的文本发生更新,但在第四个文本控件中输入文本时,其他几个文本控件都没有更新。
<!--在设置Text属性的引号内使用左右花括号将扩展标记括起来-->
<!--设置Binding的ElementName属性为需要绑定的控件-->
<!--设置Path属性为需要绑定的控件属性名称-->
<!--设置Mode属性为绑定的方式-->
<TextBlock Text="Text1" FontSize="20" />
<TextBox Name="textBox1" Margin="60" FontSize="20" Text="{Binding ElementName = textBox2, Path = Text, Mode=OneWay}"/>
<TextBlock Text="Text2" FontSize="20" Grid.Column="1" />
<TextBox Name="textBox2" Margin="60" FontSize="20" Grid.Column="1" Text="{Binding ElementName = textBox3, Path = Text, Mode=TwoWay}"/>
<TextBlock Text="Text3" FontSize="20" Grid.Row="1" />
<TextBox Name="textBox3" Margin="60" FontSize="20" Grid.Row="1" Text="{Binding ElementName = textBox4, Path = Text, Mode=OneWayToSource}"/>
<TextBlock Text="Text4" FontSize="20" Grid.Row="1" Grid.Column="1" />
<TextBox Name="textBox4" Margin="60" FontSize="20" Grid.Row="1" Grid.Column="1" Text="{Binding ElementName = textBox1, Path = Text, Mode=OneTime}"/>
本实例代码主要使用WPF扩展标记Binding实现了控件的数据绑定功能。也实现了TextBox控件的Text程序的互相绑定。 Mode有四种常用的绑定方式。
(1)OneWay:表示源控件影响目标控件。正如实例中的Text1的Text属性绑定到Text2的Text属性上,那么Text1为目标控件,Text2为源控件,Text2的Text属性发生改变会影响Text1的Text属性同步发生改变。
(2)TwoWay:表示源控件和目标控件互相影响。如Text2与Text3之间的互相影响。
(3)OneWaySource:表示目标控件影响源控件。如Text3影响Text4。
(4)OneTime:表示只绑定一次。
Style元素中的x:Key属性就是前面介绍的标记扩展,表示样式的唯一标识,当Button元素需要应用某种样式时,就将该Button的Style属性设置为该样式的x:Key的值
XML
<Window x:Class="WpfButtonStyle.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<!--Style的Setters属性为Setter集合,可以为Style定义一系列的Setter来设置应用了该样式的控件-->
<!--Setter一般用到了两个属性,一个是Property,表示控件的属性名称,另一个是Value,表示控件的属性值-->
<Style x:Key="Style1" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="Margin" Value="20"/>
</Style>
<!--Style的BaseOn属性,该属性表示该样式所继承的样式,实例代码中,style2继承了style1,style3和style4都继承了style2-->
<!--如果子样式中含有和父样式相同的属性,则子样式中的该属性值会覆盖掉父样式中该属性的值-->
<Style x:Key="Style2" TargetType="Button" BasedOn="{StaticResource ResourceKey=Style1}">
<Setter Property="Background" Value="Orange"/>
<Setter Property="FontSize" Value="24"/>
</Style>
<!--在Button3中内嵌了样式资源,但内嵌样式资源并没有生效-->
<Style x:Key="Style3" TargetType="Button" BasedOn="{StaticResource ResourceKey=Style2}">
<Style.Triggers>
<!--触发器所绑定的是Button控件的IsMouseOver属性,当IsMouseOver属性值为True时,则触发该触发器,
设置Button的背景为黄色。MultiTrigger类则表示多条件触发器,原理与Trigger类类似,只是它可以设置多个条件,
当多个条件都满足时,才触发Setter改变控件样式-->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="70"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="Style4" TargetType="Button" BasedOn="{StaticResource ResourceKey=Style2}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsPressed" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="LightGreen"/>
<Setter Property="FontSize" Value="60"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<!--如果子样式中含有和父样式相同的属性,则子样式中的该属性值会覆盖掉父样式中该属性的值。
另外,还可以看到,Button2应用了样式style2,style2中定义了Backgroud属性,而Button2中也为Background属性赋值了,
从按钮二显示的效果可以看出,Button2的Background属性是以Button2中设置的值为准。
在Button3中内嵌了样式资源,但内嵌样式资源并没有生效。从以上效果可以说明,子类中的样式可以覆盖父类中的样式,
而按钮属性中设置的值优先于样式中设置的属性值,样式中设置的属性值优先于内嵌样式中设置的属性值。-->
<Button Style="{StaticResource ResourceKey=Style1}" Name="Button1" Content="第一个按钮"/>
<Button Style="{StaticResource ResourceKey=Style2}" Name="Button2" Content="第二个按钮" Grid.Column="1" Background="Lime"/>
<Button Style="{StaticResource ResourceKey=Style3}" Name="Button3" Content="第三个按钮" Grid.Row="1">
<Button.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Yellow"/>
</Style>
</Button.Resources>
</Button>
<Button Style="{StaticResource ResourceKey=Style4}" Name="Button4" Content="第四个按钮" Grid.Row="1" Grid.Column="1"/>
</Grid>
</Window>
MainWindow 2024-05-05