XAML赋值浅析
XAML语言是一种声明性语言 也就是说,你每见到一个标签,我们的XAML编译器就会为我们创建一个相应的对象
如下 XAML编译器就为我们创建了两个对象(不是变量是对象!)
<Window x:Class="WPF_MVVMXueXiBiJi.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:WPF_MVVMXueXiBiJi"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
我们都知道对象用来存储数据的地方有两个地方
- 字段
- 属性
而向外暴露的都是属性向外暴露,因为字段不够安全,字段只能单纯的存储数据,并不具有对数据进行校验的功能
比如说我有人这个类,人都有年龄,而如果我使用字段向外暴露数据的话,那么这个字段就完全有可能被赋值为1000,但人的年龄不可能为一千,
而我们使用属性向外暴漏的话,我们就可以在属性的set这个语句里面对传进来的数值进行检验,如果过大或过小,就抛出异常,不让有问题的数据污染我们的程序
上面编译器为我们创建的Window ,Grid对象也具有很多属性,而在XAML里面我们有三种方法为对象的属性赋值
Atttribute=Value方式为对象属性赋值
实例: 我们使用Atttribute=Value的方式,将Window对象的Title这个属性赋值了MainWindow,Height属性赋值了450 ,Width属性赋值了800
<Window x:Class="WPF_MVVMXueXiBiJi.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:WPF_MVVMXueXiBiJi"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
代码效果:
如图 Window的Title是MainWindow,高是450,width是800
Atttribute=Value赋值方式还有复杂一点的
实例: 给Path 对象的Data属性赋值,起点M位置为X:0 Y:0 L用直线 画到X:200,Y:200,L用直线画到X:100,Y200,Z闭合,这是Atttribute=Value赋值方式最复杂的了,如果需要赋的值再复杂就需要使用使用属性标签的方式了
<Window x:Class="WPF_MVVMXueXiBiJi.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:WPF_MVVMXueXiBiJi"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Path Data="M 0,0 L 200,200 L 100,200 Z" Stroke="Aqua"></Path>
</Grid>
</Window>
实现效果
为什么非字符串类型的属性能使用Atttribute=Value赋值方式为其赋值
如果你仔细观察的话,你就能发现,Atttribute=Value赋值方式所赋的值都是字符串类型的值,但这个属性并不是string类型的属性,如刚刚上面使用Atttribute=Value赋值方式赋值的Data属性它是Geometry类型的属性,但是我们使用Atttribute=Value赋值方式所赋的值的类型是string类型
那它们是怎么映射过来的呢?
下面通过一个例子来讲解
实现先在后台代码生成一个Human类,类里面有两个属性,string类型的Name ,和Human类型的Child,
很显然,如果一个Human具有Child 属性,那么Child 属性也具有自己的Name属性
public class Human
{
public string Name { set; get; }
public Human Child { get; set; }
}
然后在XAML代码里使用标签声明一个Human对象
我们首先需要使用硬编码将刚刚声明的Human类所在的 WPF_MVVMXueXiBiJi 命名空间引入进来
xmlns:local="clr-namespace:WPF_MVVMXueXiBiJi"
一般情况下把当前程序集包含的命名空间映射为local,当然,这个映射名爱咋取名咋取名,只是叫local的话意义比较明确
引入好了命名空间之后我们就可以为Human类声明对象了
我们先将Human作为资源声明出来,代码如下
<Window.Resources>
<local:Human x:Key="Human" Name="Tim"></local:Human>
</Window.Resources>
然后声明一个Button按钮并让我们点击按钮的时候弹出弹窗显示Human的属性
声明按钮
<Button Content="show!" Width="100" Height="50" Click="Button_Click"></Button>
后台Click事件逻辑部分
this.FindResource(“Human”)调用窗体自己的FindResource方法获得Key值为Human的对象
is Human human判断FindResource方法获得是否是Human类型
如果是则弹出human的Name属性
private void Button_Click(object sender, RoutedEventArgs e)
{
if (this.FindResource("Human") is Human human)
{
MessageBox.Show(human.Name);
}
return;
}
效果
点击show后弹出窗弹出了human对象的name
这个很简单,因为Huamn类的Name属性本来就是字符串类型,那如果我们使用Atttribute=Value赋值方式给Huamn类的Child属性赋值呢?它能不能将赋值给Child属性的字符串转换为Child的Name属性?(Child属性是Huamn类型)
如下,如果它足够智能的话,它会转化Child类型并将赋值给他的字符串转化为Child的Name属性
前台XAML属性
<local:Human x:Key="Human" Name="Tim" Child="Little Tim"></local:Human>
点击Show按钮后,抛出异常
它的意思是不能使用Atttribute=Value赋值方式将字符串赋值给Human类型的Child属性
但是我们明明在前面使用Atttribute=Value赋值方式给Geometry类型的Data属性赋值了,
这个时候就需要使用类型转换了
首先声明一个类让他派生自TypeConverter并重写他里面的ConvertFrom方法
public class NameToHumanTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string name = value.ToString();
Human child = new Human();
child.Name = name;
return child;
}
}
声明完这个类之后将它作为特性,附加至Human这个类上面去
[TypeConverterAttribute(typeof(NameToHumanTypeConverter))]
public class Human
{
public string Name { set; get; }
public Human Child { get; set; }
}
再改一下button的点击事件,现在不show Human的Name了show Child的Name
private void Button_Click(object sender, RoutedEventArgs e)
{
if (FindResource("Human") is Human human)
{
MessageBox.Show(human.Child.Name);
}
return;
}
效果图
好了,现在我们就使用Atttribute=Value赋值方式将字符串赋值给Human类型的Child属性了,
属性标签方式赋值
XAML标签是用来声明对象的,而属性标签并不用来声明对象,而是对应着对象的属性
实例如下
<Button Width="100" Height="50" Click="Button_Click">
<Button.Content>
<Rectangle Width="20" Height="20" Stroke="DarkGreen" Fill="LawnGreen"></Rectangle>
</Button.Content>
</Button>
效果图
还有复杂一点的
<Rectangle Width="200" Height="200" Stroke="Blue">
<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>
<GradientStop Offset="0.7" Color="DarkBlue"></GradientStop>
<GradientStop Offset="1.0" Color="Blue"></GradientStop>
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
效果图
标签拓展
使用标记扩展轻松实现XAML页面属性赋值,资源引用,类型转换等操作。
常用的标记扩展:
- Binding ——> XAML载入时,将数据绑定到XAML对象;
- StaticResorce ——> 引用数据字典中定义的静态资源;
- TemplateBinding ——> XAML页面中对象模板绑定调用;
- RelativeSource ——> 对特定数据源引用;
运用与属性:
ElementName
-
ElementName
-
Path
实际运用:
<StackPanel>
<Button x:Name="Test" Width="100" Height="61.8" VerticalAlignment="Bottom" Background="SkyBlue"></Button>
<Border Margin="10" BorderBrush="Black" BorderThickness="1"></Border>
<Button Content="binding测试"
Height="61.8"
VerticalAlignment="Top"
Background="{Binding ElementName=Test,Path=Background}"
Width="{Binding ElementName=Test,Path=Width}"></Button>
</StackPanel>
因为第二个按钮的属性“Background”和“Width”的值都获取要用作绑定第一个按钮为源对象,所以第二个按钮的属性“Background”和“Width”的值直接继承第一个按钮的这两个属性的值:
StaticResorce
StaticResorce:控件需要通过StaticResorce引用在窗口中自定义的静态资源
因为资源样式中设置的是背景颜色“Background”,所以在Binding的作用下第二个按钮的“Background”属性值也会随之改变:
TemplateBinding:
代码
自定义视觉树,利用TemplateBinding实现图标按钮:
RelativeSource:
RelativeSource:实现标记扩展,以描述绑定源相对于绑定目标的位置。
代码
实现效果