属性(Property)元素
WPF的一大特色就是支持丰富的组合,下面的例子创建了一个中间是一个方块的按钮(按钮的标题已经不仅仅是文本了):
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
System.Windows.Shapes.Rectangle r = new System.Windows.Shapes.Rectangle();
r.Width = 40;
r.Height = 40;
r.Fill = System.Windows.Media.Brushes.Black;
b.Content = r; // 将按钮的内容设置为方块
按钮的Content属性是System.Object类型的,因此可以放置这个方块。通过代码我们可以非常轻松地设置按钮的内容,但是我们应该如何通过XAML来实现相同的工作呢?我们可以给Content属性设置什么字符串值才能够使之与前面的C#代码等价呢?实际上,并不存在这样的字符串值。不过WPF提供了另一种设置这种复杂属性值的语法:属性元素(property element),如:
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button.Content>
<Rectangle Height="40" Width="40" Fill="Black" />
</Button.Content>
</Button>
现在Button的Content属性是XML元素而不是XML属性,这段XAML代码与前面的C#代码等价。属性元素的形式总是TypeName.PropertyName,它们总是包含在TypeName对象元素之中,且它们不能包含自己的XML属性。
属性元素也可以用于简单属性,如下两段XAML代码完全等价:
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Content="OK" Background="White" />
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button.Content>OK</Button.Content>
<Button.Background>White</Button.Background>
</Button>
类型转换器
上面例子的等价C#代码如下:
System.Windows.Controls.Button b = newSystem.Windows.Controls.Button();
b.Content = "OK";
b.Background = System.Windows.Media.Brushes.White;
注意到XAML中的Background属性,“White”字符串是如何与System.Windows.Media.Brushes.White等价的呢?这个用法展示了一种在XAML中使用字符串设置属性的方法。需要特别注意的是,需要被设置的属性的类型并非System.String或System.Object。在这种情况下,XAML解析器/编译器必须去寻找类型转换器。类型转换器可以把字符串转换成需要的数据类型。WPF为许多公共类型提供了类型转换器,如Brush等。它们都从TypeConverter派生(如BrushConverter等)。当然,我们也可以为自己的类型编写相应的类型转换器。与XAML不同,类型转换器的名称通常支持大小写不敏感的字符串。
如果Brush没有类型转换器,我们就必须使用属性元素语法在XAML中设置Background属性了,如下:
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Content="OK">
<Button.Background>
<SolidColorBrush Color="White" />
</Button.Background>
</Button>
同样地,当Color属性没有类型转换器的时候,我们必须如下编写XAML代码:
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Content="OK">
<Button.Background>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color A="255" R="255" G="255" B="255" />
</SolidColorBrush.Color>
</SolidColorBrush>
</Button.Background>
</Button>
上面的例子仍旧需要类型转换器来转换“255”(A、R、G、B属性)到Byte,否则程序将不能继续。因此,类型转换器不仅增强了XAML的可读性,而且使得上述概念得以实现,这是其他方式做不到的。
尽管C#代码可以产生与XAML相同的结果,但在程序代码当中,并非使用了相同的类型转换机制。下面的代码非常准确地展示了在运行时返回并执行合适的类型转换器:
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
b.Content = "OK";
b.Background = (Brush)System.ComponentModel.TypeDescriptor.GetConverter(
typeof(Brush)).ConverterFromInvariantString("White");
注意:拼错“White”不会导致编译错误(在VS中会有警告),但会导致运行时异常。