(三)WPF - XAML

XAML:可扩展应用程序标记语言

1、XAML 编译

BAML实际上就是 XAML 的二进制表示。(BAML 二进制应用程序标记语言)所有 XAML 文件都被转换为 BAML,并且这些 BAML 然后作为资源被嵌入到最终的 DLL 或 EXE 程序集中。

XAML标准:

  • XAML 文档中的每个元素都映射为 .NET 类的一个实例。元素的名称也完全对应于类名。例如,元素<Button>指示 WPF 创建 Button 对象;
  • 与所有 XML 文档一样,可在一个元素中嵌套另一个元素。
  • 可通过特性(attribute)设置每个类的属性(property)。

XAML 顶级元素:

  • Window 元素
  • Page 元素(可用于导航)
  • Application 元素(该元素定义应用程序资源和启动设置)

XAML 名称空间
在创建的所有 WPF XAML 文档中都会使用这两个名称空间:

  • http://schemas.microsoft.com/winfx/2006/xaml/presentation 是 WPF 核心名称空间。它包含了所有 WPF 类,包括用来构建用户界面的控件。
  • http://schemas.microsoft.com/winfrx/2006/xaml 是 XAML 名称空间。它包含各种 XAML 实用特性,这些特性可影响文档的解释方式。该名称空间被映射为前缀x。

1.InitalizeComponent()方法
InitalizeComponent()方法的所有工作就是调用 System.Windows.Application 类的 LoadedComponent()方法。LoadComponent()方法从程序集中提取 BAML(编译过的 XAML),并用它来构建用户界面。当解析 BAML 时,它会创建每个控件对象,设置其属性,并关联所有事件处理程序。

2、使用自定义.NET 类

在后台CS代码:

 public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public override string ToString()
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }
    }

在XAML:

        <ListBox>
            <local:Person FirstName="C" LastName="RongQ"/>
            <local:Person FirstName="C" LastName="Test"/>
        </ListBox>

3、把属性用作特性

只要属性的类型可以表示为字符串,或者可以把字符串转换为属性类型,就可以把属性设置为特性。

  <Button Content="Click me" Background="LightGoldenrodYellow"></Button>

4、把属性用作元素

比如Button中Background用作为子元素Button.Backround。

 <Button>
            <Button.Background>
                <LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0">
                    <GradientStop Offset="0" Color="Yellow"/>
                    <GradientStop Offset="0.3" Color="Orange"/>
                    <GradientStop Offset="0.7" Color="Red"/>
                    <GradientStop Offset="1" Color="DarkRed"/>
                </LinearGradientBrush>
            </Button.Background>
 </Button>

5、使用集合和XAML

设置 GradientStop 为属性,并把 GradientStopCollection 元素设置为它的子元素。

  <Button>
            <Button.Background>
                <LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0">
                    <LinearGradientBrush.GradientStops>
                        <GradientStopCollection>
                            <GradientStop Offset="0" Color="Yellow"/>
                            <GradientStop Offset="0.3" Color="Orange"/>
                            <GradientStop Offset="0.7" Color="Red"/>
                            <GradientStop Offset="1" Color="DarkRed"/>
                        </GradientStopCollection>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Button.Background>
 </Button>

6、复杂属性

元素属性包含一组子属性;

<Grid Name='grid1'>
<Grid.Background>
<LinearGradientBrush>
</LinearGradientBrush>
</Grid.Background>
</Grid>

7、标记扩展

使用标记扩展访问资源。
有些情况下,不可能硬编码属性值。例如,可能希望将属性设置为一个已经存在的对象,或者可能希望通过将一个属性绑定到另一个控件来动态地设置属性值。

标记扩展可用于嵌套标签或 XML 特性中(用于 XML 特性的情况更常规)。当用在特性中时,它们总是被花括号{}包围起来。

例如:

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

标记扩展使用{标记扩展类 参数}语法;
所有标记扩展都由继承自 System.Windows.Marup.MarkupExtension 基类的类实现。MarkupExtension 基类十分简单——它提供了一个简单的ProvideValue()方法来获取所期望的数值。

当 XAML 解析器遇到上述语句时,它将创建 StaticExtension 类的一个实例(传递字符串 SystemColors.ActiveCaptionBrush 作为构造函数的参数),然后调用 ProvideValue()方法获取 SystemColors.ActiveCaption.Brush 静态属性返回的对象。最后使用检索的对象设置 cmdAnswer 按钮的Foreground 属性。

可用cs代码:

cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;

也可用作嵌套属性:

<Button>
<Button.Foreground>
<x:Static Member="SystemColors.ActiveCaptionBrush"></x:Static>
</Buttom.Foreground>
</Button>

8、创建自定义标记扩展

在后台CS代码:

public enum Operation
    {
        Add,
        Subtract,
        Multiply,
        Divide
    }

    [MarkupExtensionReturnType(typeof(string))]
    public class CalculatorExtension : MarkupExtension
    {
        public CalculatorExtension()
        {

        }
        public double X { get; set; }
        public double Y { get; set; }
        public Operation Operation { get; set; }
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget provideValue =
                serviceProvider.GetService(typeof(IProvideValueTarget))
                as IProvideValueTarget;
            if(provideValue != null)
            {
                var host = provideValue.TargetObject as FrameworkElement;
                var prop = provideValue.TargetProperty as DependencyProperty;
            }

            double result = 0;
            switch(Operation)
            {
                case Operation.Add:
                    result = X + Y;
                    break;
                case Operation.Subtract:
                    result = X - Y;
                    break;
                case Operation.Multiply:
                    result = X * Y;
                    break;
                case Operation.Divide:
                    result = X / Y;
                    break;
                default:
                    throw new ArgumentException("invalid operation");
            }
            return result.ToString();
        }
    }

在XAML:

      <TextBlock Text="{local:Calculator Operation=Add,X=3,Y=4}"/>
        <TextBlock>
            <TextBlock.Text>
                <local:CalculatorExtension>
                    <local:CalculatorExtension.Operation>
                        <local:Operation>Multiply</local:Operation>
                    </local:CalculatorExtension.Operation>
                    <local:CalculatorExtension.X>7</local:CalculatorExtension.X>
                    <local:CalculatorExtension.Y>11</local:CalculatorExtension.Y>
                </local:CalculatorExtension>
            </TextBlock.Text>
        </TextBlock>

9、附加属性

附加属性可用于多个控件但在另一个类中定义的属性。在 WPF 中,附加属性常用于控件布局。

附加属性始终使用包含两个部分的命名形式:定义类型.属性名。这种命名形式也用于区分开普通属性和附加属性。

<TextBox Grid.Row="0">
<!--Grid中的Row属于附加属性-->
</TextBox>

10、加载和编译 XAML

创建 WPF 应用程序可使用三种不同的编码方式:

  • 只使用代码。

  • 使用代码和未经编译的标记(XAML)。 处理某些特殊情况时。例如创建高度动态化的用户界面。这种方式在运行时使用 System,Windows.Markup 名称空间中的 XamlReader 类,从 XAML 文件中加载部分用户界面。

  • 使用代码和编译过的标记(BAML)。 对这种方式为每个窗口创建一个 XAML 模板,这个 XAML 模板被编译为 BAML,并嵌入到最终的程序集中。编译过的 BAML 在运行时被提取出来,用于重新生成用户界面。

(1)只使用代码

优点: 可以随意定制应用程序。缺点:编写代码麻烦。

(2)使用代码和未经编译的 XAML

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Buttom Name="button1" Margin="30">Please click me.</Buttom>
<DockPanel>

参数xamlFile作为上述的xaml文件,以下为动态加载 XAML 代码。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.IO;

public class Window1:Window
{
    private Button button1;
    public Window1()
    {
        InitializeComponent();
    }

    public Window1(string xamlFile)
    {
        //Configure the form.
        this.Width = this.Height = 285;
        this.Left = this.Top = 100;
        this.Title = "Dynamically Loaded XAML";

        //Get the XAML content from an external file.
        DependencyObject rootElement;
        using (FileStream fs = new FilesStream(xamlFile,FileMode.Open))
        {
            rootElement = (DependencyObject)XamlReader.Load(fs);
        }
        //Insert the markup into this window.
        this.Content = rootElement;
        //Find the control with the appropriate name.
        button1 = (Button)LogincalTreeHelper.FindLogicalNone(rootElement,"button1");

        //Wire up the event handler.
        button1.Click += button1_Click;
    }

    private void button1_Click(object sender,RoutedEventArgs e)
    {
        button1.Content = "Thank you.";
    }

}

操纵元素,如按钮,需要在动态加载的内容中查找相应的控件对象。

第一种方式:LogicalTreeHelper。

button1 = (Button)LogincalTreeHelper.FindLogicalNode(rootElement,"button1");

第二种方式:FrameworkElement.FindName()方法。

FrameworkElement frameworkElement = (FrameworkElement) rootElement;
button1 = (Button)frameworkElement.FindName("button1");

注意: 如果使用这种方法,确保松散的 XAML 文件不会像传统的 XAML 文件那样被编译或嵌入到项目中。
先将 XAML 编译为 BAML,再运行时加载 BAML,比动态加载 XAML 的效率高,当用户界面比较复杂时尤其如此。

(3)使用代码和编译过的 XAML
当编译 WPF 应用程序时, Visual Studio 使用分为两个阶段的编译处理过程。第一阶段将 XAML 文件编译为 BAML 。第二阶段Visual Studio 使用合适的语言编译器来编译代码和生成的部分类文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值