WPF高级教程(三)XAML

语言简介

  • XAML(Extensible Application Markup Language) 是用来写界面的
  • XAML是大小写敏感的
  • WPF是前后端分离的,前端用XAML实现,后端用C#写(注意能用XAML写的都能用C#实现,但是用XAML更加直观,我们在本篇教程里,一般只说明使用XAML的实现,如果大家需要在后台更改界面,可以自行搜索XAML对应的C#实现)

命名空间

格式

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  • http:// 并不是一个网址,而是十几个命名空间的集合,这样做,既保证了这个Uri不会于用户自定义的命名空间重复(因为schemas.microsoft.com是微软所有的)也降低了文档的复杂程度,不用写十几个命名空间了。
  • xmlns:x 表示这个命名空间的别名是x,在使用的过程中可以通过x:Name使用这个命名空间下的对象
  • presentation 是WPF核心命名空间,没有前缀,默认(像Button这种)都在这个命名空间之内
  • xaml 是XAML的命名空间

不同项目中的命名空间的引用

xmlns:Prefix="clr-namespace:Namespace;assembly=AssemblyName"

注意AssemblyName是程序集名称,只要在该项目中引用过即可使用。如果就在本程序集中,则可以忽略这个选项。

// 把当前项目程序集设置为local
xmlns:local="clr-namespace:MyNamespace"

类名 部分类

x:Class="Novc.ViPlex.Express.View.AboutView"

这个声明使得XAML生成了一个部分类,与后台的部分类一起构成了完整的窗体类定义

XAML加载

InitializeComponent方法

  • 这个方法的作用是加载XAML并且构建用户界面
  • 当自定义构造函数的时候必须写上这个方法

事件和属性

XAML加载的顺序是先设置Name属性,再给元素挂载事件,最后加载属性。所以,如果监听了某个属性的变化的事件,在第一次设置该属性的时候(加载属性的时候),事件都会触发一次。

属性的类型转换器(TypeConverter)

在XAML中,属性的设置全部是字符串类型的,但是属性的类型却是多种多样的,比如Image的Source是一个BitmapImage类型,VerticalAlignment是枚举类型。为了解决这个问题,.NET引入了类型转换器的概念

类型转换器存在于

  • 需要设置的属性中,比如 Background 属性
  • 属性设置的值中,比如Background需要使用一个Brush对象,XAML也会检测Brush中是否有类型转换器

属性的设置

  1. 直接设置属性(使用类型转换器)

    HorizontalAlignment="Left"
    
  2. 设置复杂属性
    属性元素语法,语法规则为Parent.Property,用于设置一个复杂的,对象类型的属性值。

    <Grid HorizontalAlignment="Left" Height="100" Margin="196.617,195.39,0,0" VerticalAlignment="Top" Width="118.795">
        <Grid.Background>
            <LinearGradientBrush>
                <GradientStop Offset="0.00" Color="Red"></GradientStop>
                <GradientStop Offset="0.50" Color="Indigo"></GradientStop>
                <GradientStop Offset="1.00" Color="Violet"></GradientStop>
            </LinearGradientBrush>
        </Grid.Background>
    </Grid>
    
  3. 使用标记扩展设置属性(不太常用,知道即可)

    当我们要给一个属性设置为一个已经存在的属性的时候使用,例如,A类中有一个静态的Brush,B.xaml中需要使用A中的Brush,需要注意的是,A中的Brush必须是静态的,即通过类名就可以访问。

    <Button Foreground="{x:Static SystemColors.ActiveCaptionBrush}"/>
    

    注意上面的语法,Static是StaticExtension的简写,即静态扩展,在后台中这样写

    cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;
    

    作为嵌套属性这样写,注意需要使用Member:

    <Button.Foreground>
        <x:Static Member="SystemColors.ActiveCaptionBrush"/>
    </Button.Foreground>
    
  4. 设置附加属性

    简单说,附加属性就是一些属性只有在某种特性情况下才能被设置,比如控件在Grid中的时候才能设置Grid.Row。

    这里我们多分析两句,想必设置Grid.Row人人都会,但是为什么它叫附加属性呢?因为TextBox中本来没有这个属性,Background,TextAlignment这种都属于控件的固有属性,而Grid.Row这种是添加进去的。这个属性不在FrameworkElement这样的基类中,而在DependencyObject中有一个字典,这个字典中保存所有扩展属性的Key和Value,因为保存它的是一个字典,所以它可以扩展,那么设置Grid.Row就等价于下面的代码

    txtQuestion.SetValue(Grid.RowProperty, 1)
    

    为什么不写在基类中,要写成这样呢?很简单,因为不是所有的组件都需要这个属性,只有在Grid中的组件有可能需要设置这个属性,所以,做成可扩展的就不扰乱基类,也可以方便扩展。

元素树

我们知道元素是以嵌套的方式进行构建的,构建的方式是:

  • 如果父元素实现了IList接口,就调用IList.Add
  • 如果父元素实现了IDictionary接口,就调用IDictionary.Add
  • 如果父元素使用了ContentProperty修饰,子元素设置对应的属性

我们最需要关注的就是ContentProperty属性,因为ContentControl,ItemsControl,Panel基类都使用ContentProperty特性,这就是说几乎所有的控件都使用ContentProperty,而IList接口的多是属性等,在设置属性的时候可以设置一个集合。

对于Grid中加入各种元素的情况,并不是因为Grid是IList的集合,Grid支持的是ContentProperty,而能加入很多元素是因为Grid继承自Panel,Grid使用Panel渲染ContentProperty属性,实现可以添加多个元素。Panel有一个Children属性是一个集合,添加元素就是往Children中加入元素,看以下代码

txtQuestion = new TextBox();
grid.Children.Add(txtQuestion);

那什么父元素是IList的呢?之前就说过了,比如之前的GradientStopCollection,里面可以添加很多个GradientStop

对于Grid我们说过了,那对于简单元素,就更简单了。TextBox的ContentProperty渲染的是Text属性,Button的ContentProperty渲染的是Content属性,这里我们要讨论以下Text属性和Content属性的区别了。

Text 属性和 Content属性

当我们指定控件中的文字的时候,我们发现有一些控件需要指定Text属性,有一些控件却是要指定Content属性,那么这两个属性有什么区别呢?其实区别很简单,Text属性只能接受string类型的值,那么控件中就只能添加文字,Content属性就比较丰富多彩,可以放各种各样的东西。看下面的例子说明一下。

<Button>
    <Button.Content>
        <Image Source="/Image/1.jpg"></Image>
    </Button.Content>
</Button>

实体字符集

< > " & 这些作为XAML的关键字,不能在XAML中直接显示,当我们要显示这些字符的时候需要用到实体字符标记。
在这里插入图片描述

<Button>
    &lt;Click&gt;
</Button>
// 显示出来的是<Click>

注意在代码中设置Content不用遵循这个规则,而是需要遵循C#的转义规则。

空白处理

默认

折叠所有空白(打多少空格都只显示一个),如果想要显示所有的空格,有下面三种办法

  1. 在代码中设置的空格多少个都会被保留
  2. 使用 xml:space=“preserve” 属性
  3. 在属性中设置的多少个空格都会被保留
// 空格会保留
<TextBlock Text="[1     3      5         7]" />

// 空格会保留
<TextBox xml:space="preserve">
    1   2   3   4 
</TextBox>
// 空格不会保留
<TextBox>
    1   2   3   4 
</TextBox>

使用代码控制XAML

// 加载XAML  XamlReader
using(FileStream fs = new FileStream(xamlFile. FileMode.Open))
{
    rootElement = (DependencyObject)XamlReader.Load(fs);
}
// 查找元素方法1
button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1");
// 查找元素方法2
FrameworkElement frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");

注意事项

  • 在XAML中加载的类,最好都能有一个无参数的构造函数,这样的类适合XAML的加载
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章