• WPF笔记(一)之初识XMAL


    WPF笔记(一)之初识XMAL

    本人对Java更熟悉,所以写的时候会不自觉与Java对比

    XAML原理

    XAML是XML的扩展,在WPF中XAML用于绘制UI,可以理解为用XAML去定义C#的UI类,在XAML中声明了一个标签,就意味着创建了一个对象。

    比如WPF项目刚创建好时,XAML为

    <Window x:Class="MyWpfApp.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            
        </Grid>
    </Window>
    

    上面例子中有<Window><Grid>两个标签,就意味着创建了System.Windows.WindowSystem.Windows.Controls.Grid的对象,然后<Window>标签上定义了一些属性。

    一般来讲,XAML中的属性叫Attribute,对象中的属性叫Property,对象中声明的私有变量叫字段field。Java中没有属性,而是使用Get/Set方法进行了代替。

    然后是标签的属性,一行一行来说。
    首先是Title、Height、Width这一行,对应Window对象的标题和大小,不再多说。
    然后是xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation",其中xmlns指的是名称空间,代表引用哪些名称空间。这里值看似是一个网址,其实是硬编码到XAML编译器中的,XAML解析时看到这个网址的名称空间就会自动把一些UI相关程序集和名称空间引入进来。
    接着是xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"这里xmlns:x加了一个x的区分,用来表示不同名称空间,加了区分之后,使用这个名称空间的内容时就需要把区分作为前缀添加的标签上。这一行引用的名称空间里面主要是一些XAML解析需要的程序集。
    然后是x:Class这一行,代表在MyWpfApp命名空间中创建一个MainWindow的类,和MainWindow.xml.cs中的MainWindow是同一个类,继承自Window。

    可以在MainWindow.xml.cs中看到MainWindow使用了partial来声明,这个关键字用来在不同文件中声明同一个类,Java中没有这个功能

    了解上面的信息之后,对一个最简单的XAML程序的结构就清楚了。

    XAML语法基础

    XAML中有两种方法给对象的属性赋值:

    • 使用字符串进行简单赋值
    • 使用元素属性(Property Element)进行复杂赋值

    使用简单字符串进行赋值的例子:

    <Rectangle x:Name="rectangle" Width="200" Height="150" Fill="Blue"/>
    

    这是一个矩形的控件,其中有一个Fill属性用来填充内部的颜色,Rectangle类的Fill属性是Brush类型,Brush的子类很多,比如SolidColorBrush:单色笔刷。所以上面的属性Fill="Blue"就相当于下面的C#代码

    ...
    SolidColorBrush sBrush = new SolidColorBrush();
    sBrush.Color = Color.Blue;
    this.rectangle.Fill = sBrush;
    ...
    

    可以看到Blue这个字符串最终被翻译成了一个SolidColorBrush对象,并赋值给了rectangle。

    那么为什么是SolidColorBrush而不是其他Brush子类呢?并且如果要设置的对象比较复杂的时候,字符串不就很复杂、很难写了吗?

    使用TypeConverter类将XAML中的Attribute转换为对象的Property

    对于第一个问题,字符串如何解析的逻辑由TypeConvert决定。

    举个例子,现在有一个可以在XAML中声明的类Human:

    public class Human
    {
        public string Name { get; set; }
        public Human Child { get; set; }
    }
    

    想要在XAML中创建出这个类的对象,并且给Child属性赋值,Child的名字叫“ABC”:

    <Window.Resources>
        <local:Human x:Key="human" Child="ABC"/>
    </Window.Resources>
    

    直接这么写是编译不过去的,因为XAML解析器并不知道怎么把字符串转化为Human类型,此时就需要TypeConvert类决定如何转换:

    public class StringToHumanTypeConverter: TypeConverter
    {
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string name)
            {
                Human h = new Human();
                h.Name = name;
                return h;
            }
    
            return base.ConvertFrom(context, culture, value);
        }
    }
    

    Visual Studio 中重写方法时,直接public override就会有提示(IDEA用多了总想着Ctrl+i、Ctrl+o)

    然后在Human类上添加上特性:

    [TypeConverter(typeof(StringToHumanTypeConverter))]
    public class Human
    {
        public string Name { get; set; }
        public Human Child { get; set; }
    }
    

    这样,XAML解析器就知道怎么把“ABC”这个字符串转化为Human对象,然后赋值给Child属性了。

    C#中类上面被中括号包起来的叫特性,类似Java中的注解

    使用属性元素方式进行属性赋值

    XAML中,非空标签都有自己的内容(Content),内容中的子级标签都是副标签内容的一个元素(Element),那么属性元素的意思就是把一个元素当做属性。

    也就是上面矩形的例子用属性元素改写一下,将会是这样:

    <Rectangle x:Name="rectangle" Width="200" Height="150">
        <Rectangle.Fill>
            <SolidColorBrush Color="Blue"/>
        </Rectangle.Fill>
    </Rectangle>
    

    <Rectangle.Fill>标签代表要给Rectangle的Fill属性赋值,赋给的值就是标签括住的内容。
    与之前的代码效果完全一致,这种写法主要用来设置属性复杂的对象。

    比如给矩形设置一个渐变的颜色
    <Rectangle x:Name="rectangle" Width="200" Height="150">
        <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 Offset="0.7" Color="Pink"/>
                        <GradientStop Offset="1.0" Color="Blue"/>
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    

    可以看到这样自由度非常高。

    标记扩展

    大多数的属性赋值都是为属性生成一个新的对象,但是有时候我们需要把一个对象赋值给两个对象的,或者一个对象的属性依赖于其他对象的属性,这时候就要使用啊扩展标记了。

    比如这里有一个文本框,以及一个滑动条,想要的效果是吧滑动条的值显示在文本框里:

    <TextBox Text="{Binding ElementName=slider1, Path=Value, Mode=OneWay}"/>
    <Slider x:Name="slider1" Margin="5"/>
    

    上面的代码中,Text="{Binding ElementName=slider1, Path=Value, Mode=OneWay}"就是标记扩展,这是XAML特有的语法:

    • 当XAML编程器遇到这句代码时会把花括号的内容解析成相应的对象
    • 对象的数据类型是紧邻左花括号的字符串(这里就是Binding)
    • 对象的属性由一连串逗号连接的子字符串进行初始化(属性值不再加引号)

    这个写法与C#3.0的对象初始化语法非常相似:

    Binding binding = new Binding() { ElementName=slider1, Mode=BindingMode.OneWay };
    

    几个小点:

    标记扩展是可以嵌套的:Text={Binding Source={StaticResource myDataSource}, Path=PersonName}
    标记扩展有一些简写语法,例如{Binding Value, ...}{Binding Paht=Value, ...}是等价的,{StaticResource myString, ...}{StaticResource ResourceKey=myString, ...}是等价的。第一种称为固定位置参数,第二种是具名参数。固定位置参数实际上就是类的构造器的位置。

    导入程序集

    在XAML中引入类库或其他程序集的语法是:

    xmlns:映射名="clr-namespace:类库中名称空间名;assembly=类库文件名"
    

    比如MyLibary.dll中有两个名称空间,在XAML中引用是:

    xmlns:common="clr-namespace:Common;assembly=MyLibary"
    xmlns:controls="clr-namespace:Controls;assembly=MyLibary"
    

    其中:

    • xmlns是XAML中用来声明名称空间的属性,是XML namespace的缩写。
    • xmlns:后面的映射名是自定义的,所有引用的名称空间都要加上映射名(无映射名的默认名称空间已经被XAML占用)
    • 引号中的字符串决定引用那个类库的那个名称空间(Visual Studio可以自动提示)。

    x名称空间

    • x:Class:指定XAML编译成的类与那个类结合(该类必须声明为partial)
    • x:Name:为XAML中的标签对象创建引用变量(也就是起个名字,接着就可以在xaml.cs的类中使用了)
    • x:FieldModifier:指定x:Name的作用域(public或者private之类的)
    • x:Key:在XAML中,可以把要复用的资源放在资源字典中(Resource Dictionary),然后通过key检索出来进行复用(在XAML对应的文件中也可以使用 String str = this.FindResource("aStr") as String;)。
    • x:Type:在C#中,Type类类似Java中的Class类,而x:Type就是专门给这种类型的属性赋值的(有了Type类之后就可以使用反射了)。
    • x:Null显式的给属性赋Null值(可以去除默认值或者同一赋值时排除个别控件)。
    • x:Array提供一个List(一般用作静态的DataSource)
    • x:Static引用静态成员(字段和属性都可以)

    举几个例子:

    x:Key举例
    <Window x:Class="MyWpfApp.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:MyWpfApp"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d"
            xmlns:tc="clr-namespace:MyWpfApp.TestClass"
            Title="MainWindow" Height="450" Width="800">
        <Window.Resources>
            <!-- 设定静态资源的Key -->
            <sys:String x:Key="aStr">Hello WPF</sys:String>
        </Window.Resources>
        <Grid>
            <!-- 在这里引用(如果及设置Text属性,又在标签内添加文字,运行结果是字符串拼在一块,但是在设计窗口中体现不出来) -->
            <TextBlock Text="{StaticResource ResourceKey=aStr}"></TextBlock>
        </Grid>
    </Window>
    
    
    x:Type举例

    首先创建一个被反射的类:

    class ClassForType : Window
    {
        public void SayHello()
        {
            MessageBox.Show("Hello");
        }
    }
    

    然后创建一个自定义的Button类:

    public class MyButton : Button
    {
        // Type属性,在XAML中被设置
        public Type SetMe { get; set; }
    
        // 重写Button的click时间
        protected override void OnClick()
        {
            base.OnClick();
            // 反射创建本地类型的对象(dll中的对象需要使用Assembly类进行反射创建)
            ClassForType instance = Activator.CreateInstance(this.SetMe) as ClassForType;
            instance.SayHello();
        }
    }
    

    最后在XAML中给属性进行赋值:

    <!-- tc是我自定义的名称空间 -->
    <tc:MyButton SetMe="{x:Type TypeName=tc:ClassForType}"/>
    
    x:Array

    一个把x:Array作为数据源的ListBox举例

    <ListBox>
        <ListBox.ItemsSource>
            <x:Array Type="sys:String">
                <sys:String>Tom</sys:String>
                <sys:String>Tim</sys:String>
                <sys:String>Mac</sys:String>
            </x:Array>
        </ListBox.ItemsSource>
    </ListBox>
    

    搜了一下关于x:Array的讲解并不是很多,看这个介绍好像用起来也不是特别方便,估计一般用来在XAML中写一些静态的值

    x:Static

    首先添加一个静态成员:

    namespace MyWpfApp
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            // 静态字段
            public static string Title = "高山月小";
            // 静态只读属性
            public static string AText { get { return "水落石出"; } }
    
            ...
    
        }
    }
    

    在XAML中使用:

    <Window x:Class="MyWpfApp.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:MyWpfApp"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d"
            xmlns:tc="clr-namespace:MyWpfApp.TestClass"
            Title="{x:Static local:MainWindow.Title}" Height="450" Width="800">
        <Grid>
            <TextBox Text="{x:Static local:MainWindow.AText}"/>
        </Grid>
    </Window>
    
    

    VS2017中,XAML不能识别第一个字段,但实际运行是有效果的。对C#开发规范来说,字段公开时必须以属性形式,因此不用担心这个问题。

  • 相关阅读:
    SICP的一些个人看法
    小猴爬台阶问题
    QlikView同button控制转换图表类型(例如,变成一个垂直的条形图)
    hdu1325 Is It A Tree?并检查集合
    c++头
    三个一流的设计继承
    流动python
    EL字符串表达式和常用功能用途拦截
    手提wifi双卡配置+window7同时多用户远程+有些公司限制网络环境方案
    AND信号灯和信号灯集-----同步和互斥解决面向对象(两)
  • 原文地址:https://www.cnblogs.com/lixin-link/p/13755986.html
Copyright © 2020-2023  润新知