WPF自学手册-读书笔记(二)心法

XAML和C#

实现相同的窗体的两种语言的源码

<Window Title="XAML 窗口" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width = "300" Height="200" >
    <DockPanel>
        <Button DockPanel.Dock="Left"
        Background="AliceBlue" Margin="0 5 0 10"  Content="Hello XAML"/>
        <Button >
            <!--������DockPanel��������Button-->
            <DockPanel.Dock>
                Right
            </DockPanel.Dock>
            <Button.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                    <GradientStop Color="Yellow" Offset="0.0" />
                    <GradientStop Color="Red" Offset="0.25" />
                    <GradientStop Color="Blue" Offset="0.75" />
                    <GradientStop Color="LimeGreen" Offset="1.0" />
                </LinearGradientBrush>
            </Button.Background>
            Hello XAML

        </Button>

    </DockPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace mumu_windowcs
{
    public class MyWindow : Window
    {
        [STAThread]
        public static void Main()
        {
            MyWindow win = new MyWindow();
            win.Show();

            Application app = new Application();
            app.Run();
        }
        public MyWindow()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.Title = "XAML窗口";
            this.Width = 300;
            this.Height = 200;
            DockPanel panel = new DockPanel();

            Button btn = new Button();
            btn.Content = "Hello XAML";
            btn.Background = new SolidColorBrush(Colors.AliceBlue);
            btn.Margin = new Thickness(0, 5, 0, 10);
            DockPanel.SetDock(btn, Dock.Left);
            panel.Children.Add(btn);

            Button btn2 = new Button();
            btn2.Content = "Hello XAML";
            LinearGradientBrush brush = new LinearGradientBrush();
            brush.StartPoint = new Point(0, 0);
            brush.EndPoint = new Point(1, 1);

            brush.GradientStops.Add(new GradientStop(Colors.Yellow, 0));
            brush.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
            brush.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
            brush.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1));
            btn2.Background = brush;
            DockPanel.SetDock(btn2, Dock.Right);
            panel.Children.Add(btn2);
            this.Content = panel;

        }
    }
}

XAML文件有两个重要组成部分:一是有完整的开始和结束标签的要素,如Window,DockPanel和Button等,称为“元素”(Element);二是依附于元素的要素,如Width,Height和Background等,称为“属性”(Attribute).
在XAML需要命名空间:xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation”

命名空间以及映射

在WPF中只有4种元素可以作为根元素

  • Window:一个窗体
  • Page:页面
  • Application:一个应用程序
  • ResourceDictionary:代表一个逻辑资源的集合
    在这里插入图片描述

其他的命名空间

  1. 系统命名空间:xmlns:s =“clr-namespace:System;assembly=mscorlib.dll”
  2. 自定义类
    2-1. 本地的自定义类:xmlns:local =“clr-namespace:mumu_customnamespace”
    2-2. 外部程序的自定义类:xmlns:customlib =“clr-namespace:mumu_custolib;assembly = mumu_customlib”

简单属性

  • 直接赋值的,比如像“Title”和“Width”.
  • 相当于c#中枚举型,直接字符赋值就行,比如
    C#
solidbrush.Color= Colors.AliceBlue;

XAML

<Button Color="AliceBlue" />

还有c#的枚举可以通过运算符“|”,而XAML直接用“,”。

附加属性

  • 附加属性是可以用于多个控件,但是在另外一个类中定义的属性。
  • 附加属性常用于布局,命名方式是“定义类型.属性”比如“DockPanel.Dock=“Left” ”。
  • 附加属性的设置可以使用“Attribute”和“Property-Element”语法
    比如
<Button>
	<!-- 这里是DockPanel,而不是Button-->
	<DockPanel.Dock>
	Right
	</DockPanel.Dock>
	Hello CAML
</Button>

Content属性

<Button Width = "300" Height= "200">
	<Button.Content>
		Hello XAML
	</Button.Content>
</Button>
<Button Width = "300" Height= "200" Content="Hello XAML">
</Button>
<Button Width = "300" Height= "200">
Hello XAML
</Button>

C# 自定义Content属性
在book类之前添加一个Text属性,并将其制定为Content属性。然后在ToString函数中添加Text的内容如下代码

[ContentProperty("Text")] //声明Content属性
    public class Book
    {
        public Book()
        {
        }
        //Name属性
        public string Name
        {
            get;
            set;
        }
        //Price属性的数据类型是一个MoneyType类,该类声明了类型转换器,可以将带有美元符号的价格转换为人民币
        public double Price
        {
            get;
            set;
        }
        //Text属性
        public string Text { get; set; }

        public override string ToString()
        {
            string str = Name + "售价为:" + Price + "元\n"+Text;
            return str;
        }
    }

类型转换器

字符串 -> 类型转换器 -> CLR对象
自定义类型转换器:
例如:
为Price定义一个新的类型MoneyType,它只是简单的封装了一个double类型的变量。同时提供了一个静态的Parse方法将一个字符串正确转换为MoneyType类型的对象

//声明类型转换器
    [TypeConverter(typeof(MoneyConverter))]
    public class MoneyType
    {
        private double _value;
        public MoneyType() { _value = 0; }
        public MoneyType(double value)
        {
            _value = value;
        }
        public override string ToString()
        {
            return _value.ToString();
        }
        //价格转换方法,这里只考虑美元和人民币,不考虑其他币种
        public static MoneyType Parse(string value)
        {
            string str = (value as string).Trim();
            if (str[0] == '$')
            {
                //将美元转换为人民币
                string newprice = str.Remove(0, 1);
                double price = double.Parse(newprice);
                return new MoneyType(price * 8);
            }
            else
            {
                //不带特殊符号的字符串默认识别为人民币
                double price = double.Parse(str);
                return new MoneyType(price);
            }
        }
    }
    public class MoneyConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }

        //转换为字符串类型其实不需要重写此方法
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                return true;
            }
            return base.CanConvertTo(context, destinationType);
        }

        //将string转换为MoneyType
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
                return MoneyType.Parse((string)value);
            return base.ConvertFrom(context, culture, value);

        }
        //将MoneyType转换为string
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                return ((MoneyType)value).ToString();

            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

标记扩展

现在大多数的属性XAML都可以工作得很好,但依旧会有几种情况XAML难以胜任

  • 将一个属性赋值为null.
  • 将一个属性赋值给一个静态变量
    遇到以上情况我们就需要使用标记扩展了。标记扩展是通过XAML的显式的,一致的语法调用实现。标记扩展可以通过自定义来实现XAML的语义扩展。在XAML中只要属性值被一对花括号{}括起来,XAML解析器就会认为这是一个标记扩展。
    比如
<Button Name="btn" Content ="MyButton" Click="btn_Click" Background="{x:Null}">

C#和XAML合璧

  • Markup +Code-Behind
  • 工作原理
    APP类仍从Main函数开始,XAML的名称和XAML文件中Name属性一致。在Connect方法中获得该窗口的对象的实例,并实现事件处理函数的注册。Connect方法是IComponentConnect必须实现的一个接口方法,在MainWindow的InitializeComponent函数中会根据初始的XAML文件将其转换为当前的应用对象,该函数调用LoadComponent后将一个bool类型的私有变量设置为true。以保证在程序的生命周期中请求的资源只加载一次。加载的资源是一个BAML文件,它和生成的g.cs文件在同一个目录下。BAML文件是一个二进制形式的XAML文件,它会作为一个二进制资源嵌入到程序集中,可以通过Reflector工具查到,这样做的目的是提高程序运行时的性能。

依赖属性

依赖属性是WPF引入的一个新的属性类型。是一种类型为DependcyProperty的属性,其依赖属性标识则是依赖属性的实例。
相关术语:

  • DependencyObject: WPF中的一种类型。继承该类后可以注册和拥有依赖属性。
  • WPF属性系统:WPF通过提供一系列的服务扩展了普通的.NET属性,这些服务总称为“WPF属性系统”
  • .NET属性包装器:指属性的get和set实现
    依赖属性像一个计算过程,依据输入值经过计算得到输出值,整个计算过程依赖其他属性与内在和外在的多种因素
    在这里插入图片描述

为什么要引入依赖属性

WPF主要设计思想是侧重属性胜于方法和事件。即如果属性可以解决问题,则坚决不使用方法和事件。

依赖属性的用例

该用例实现了资源引用支持,样式支持,动画支持,数据绑定的支持,属性值继承的支持,元数据重载的支持,对WPF设计器的集成支持。
APP的XAML如下

<Application x:Class="mumu_Button01.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:mumu_Button01"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
    	<!-- 引入一个.NET资源对象-->
        <local:BindingData x:Key="myDataSource"/>
        <!-- 定义一个KEY值为MyBrush的画刷资源-->
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <!-- 添加一个新的样式 "GreenButtonStyle"-->
        <Style x:Key="GreenButtonStyle">
            <Setter Property="Control.Background" Value="Green"/>
        </Style>
    </Application.Resources>
</Application>

BindingData.cs如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace mumu_Button01
{
    class BindingData
    {
        public BindingData()
        { ColorName = "Red"; }

        private string name = "Red";

        public string ColorName
        {
            get { return name; }
            set
            {
                name = value;
            }
        }

    }

}

MainWindow的XAML如下

<Window x:Class="mumu_Button01.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   
    Title="mumu_Button01" Height="269" Width="572">
    <Grid Name="Grid1">      
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--资源支持-->
        <Label HorizontalAlignment="Center" VerticalAlignment="Center">资源支持</Label>
        <Button Grid.Row="0" Grid.Column="1"  Name="resouceBtn"  Margin="5" Background="{DynamicResource MyBrush}">金色按钮</Button>
        
        <!--样式支持-->
        <Label Grid.Row="0" Grid.Column ="2" HorizontalAlignment="Center" VerticalAlignment="Center">样式支持</Label>
        <Button Grid.Row="0" Grid.Column="3" Name="styleBtn"  Padding="0" Margin="5" Style="{StaticResource GreenButtonStyle}">绿色按钮</Button>
        
        <!--动画支持-->
        <Label Grid.Row="1" Grid.Column ="0" HorizontalAlignment="Center" VerticalAlignment="Center">动画支持</Label>
        <Button Grid.Row="1" Grid.Column="1" Name="animationBtn" Margin="5">
            <Button.Background>
                <SolidColorBrush x:Name="AnimBrush"/>
            </Button.Background>
            <Button.Triggers>
                <EventTrigger RoutedEvent="Button.Loaded">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation
					            Storyboard.TargetName="AnimBrush" 
					            Storyboard.TargetProperty="(SolidColorBrush.Color)"
					            From="Red" To="Green" Duration="0:0:5" 
					            AutoReverse="True" RepeatBehavior="Forever" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Button.Triggers>
            动画按钮
        </Button>


        <!--数据绑定支持-->
        <Label Grid.Row="1" Grid.Column ="2" HorizontalAlignment="Center" VerticalAlignment="Center">数据绑定支持</Label>
        <Button Grid.Row="1" Grid.Column="3" Name="BindingBtn" Background="{Binding Source={StaticResource myDataSource},Path=ColorName}">我被绑定成红色!</Button>
        
        <!--属性值继承-->
        <Label Grid.Row="2" Grid.Column ="0" HorizontalAlignment="Center" VerticalAlignment="Center">属性继承支持</Label>
        <Button Grid.Row="2" Grid.Column="1" Name="FontSizeWinBtn" Click="FontSizeWinBtn_Click">设置窗口字体:16</Button>
        <Button Grid.Row="2" Grid.Column="2" Name="FontSizeBtn" Click="FontSizeBtn_Click">设置按钮字体:8</Button>
        <Button Grid.Row="2" Grid.Column="3" Name="ResetFontSizeBtn" Click="ResetFontSizeBtn_Click">重置字体:12</Button>
        
    </Grid>
</Window>


mainWindow.xaml.cs如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace mumu_Button01
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            _oldFontSize = FontSize;
        }
        private double _oldFontSize = 0;

        private void FontSizeWinBtn_Click(object sender, RoutedEventArgs e)
        {
            FontSize = 16;
        }

        private void FontSizeBtn_Click(object sender, RoutedEventArgs e)
        {
            this.FontSizeBtn.FontSize = 8;
        }

        private void ResetFontSizeBtn_Click(object sender, RoutedEventArgs e)
        {
            FontSize = _oldFontSize;
            //this.FontSizeBtn.FontSize = _oldFontSize;

        }
    }
}

自定义依赖属性

何时需要自定义依赖属性:需要资源引用支持,样式支持,动画支持,数据绑定的支持,属性值继承的支持,元数据重载的支持,对WPF设计器的集成支持的时候。
前置条件:

  • 该类必须继承自DependencyObject类。
  • 该类中必须定义一个public static readonly 成员变量,类型为DependencyProperty.
  • 该依赖属性名必须以“属性名+Property”命名。
  • 必须调用DependencyProperty的注册方法在WPF属性系统中注册该依赖属性或者使用依赖属性的AddOwner方法。
  • 为依赖属性实现一个.NET属性包装器

自定义Button按钮

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace mumu_Button02
{
    public class SpaceButton : Button
    {
        // 传统.Net做法 私有字段搭配一个公开属性
        string txt;

        public string Text
        {
            set
            {
                txt = value;
                Content = SpaceOutText(txt);
            }
            get
            {
                return txt;
            }
        }
        // 依赖属性
        public static readonly DependencyProperty SpaceProperty;

        //.Net属性包装器
        public int Space
        {
            set
            {
                SetValue(SpaceProperty, value);
            }
            get
            {
                return (int)GetValue(SpaceProperty);
            }
        }

        // 静态的构造函数
        static SpaceButton()
        {
            // 定义元数据
            FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
            metadata.DefaultValue = 0;
            metadata.Inherits = true;
            metadata.PropertyChangedCallback += OnSpacePropertyChanged;

            // 注册依赖属性
            SpaceProperty = DependencyProperty.Register("Space", typeof(int), typeof(SpaceButton), metadata, ValidateSpaceValue);
        }

        // 值验证的回调函数
        static bool ValidateSpaceValue(object obj)
        {
            int i = (int)obj;
            return i >= 0;
        }

        // 属性值改变的回调函数
        static void OnSpacePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            SpaceButton btn = obj as SpaceButton;
            string txt = btn.Content as string;
            if (txt == null) return;
            btn.Content = btn.SpaceOutText(txt);
        }

        // 该方法给字符串间距加上空格
        string SpaceOutText(string str)
        {
            if (str == null)
                return null;

            StringBuilder build = new StringBuilder();

            // 往里面加上Space个空格
            foreach (char ch in str)
                build.Append(ch + new string(' ', Space));

            return build.ToString();
        }
    }
}


外部调用自定义按钮

<Window x:Class="mumu_Button02.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local ="clr-namespace:mumu_Button02"
        Title="mumu_Button02" Width="300" Height="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <local:SpaceButton x:Name="btnSpace" Grid.Column="0" Grid.Row="0" Margin="5" Click="btnSpace_Click" Text="设置按钮字符空格:2">
        </local:SpaceButton>
        <local:SpaceButton x:Name= "winSpace" Grid.Column="0" Grid.Row="1" Margin="5" Click="winSpace_Click" Text="设置窗口字符空格:2">

        </local:SpaceButton>
    </Grid>

</Window>

在MainWindow.xaml.cs中添加事件

private void btnSpace_Click(object sender, RoutedEventArgs e)
        {
            this.btnSpace.Space = 2;
        }

在窗口中继承依赖属性

static MainWindow()
        {
            FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
            metadata.Inherits = true;
            SpaceProperty = SpaceButton.SpaceProperty.AddOwner(typeof(MainWindow));
            SpaceProperty.OverrideMetadata(typeof(Window),metadata);
        }

        public static readonly DependencyProperty SpaceProperty;
        public int Space
        {
            set
            {
                SetValue(SpaceProperty, value);
            }
            get
            {
                return (int)GetValue(SpaceProperty);
            }
        }

注意窗口的依赖特性Space不是通过注册而来,而是从SpaceButton的Space属性的AddOwner方法得来的,即依赖属性可以选择把自身添加给其他属性。需要特别注意原始元数据不能在使用,必须新建一个,为了实现属性值继承,将Inherit标识设置为true.

依赖属性的优先级

在这里插入图片描述

附加属性

在WPF里,最典型的附加属性就是各种布局里面的属性,比如Grid的Row,Column。
附加属性本质是依赖属性,但是与普通的依赖属性有所不同:

  • 注册不在是通过Register方法注册,而是通过RegisterAttached方法注册。
  • 没有普通的.NET属性包装器,而是通过Get和Set属性名来实现包装的
  • 没有普通的.NET属性

路由事件

路由事件的定义:“Functional definition : A routed event is a type of event that can invoke handlers on multiple in an element tree ,rather than just on the object that object that raised the event.
Implementation definition: A routed event is a CLR event that is backed by an instance of the RounedEvent class and is processed by the Windows Presentation Foundation(WPF)event system”
函数定义:路由事件是一种事件类型,它可以在元素树中的多个元素上调用处理程序,而不仅仅是在引发事件的对象上调用处理程序。
实现定义:路由事件是由RounedEvent类的实例支持并由Windows Presentation Foundation(WPF)事件系统处理的CLR事件
在这里插入图片描述

路由事件的作用

以按钮为例,在WinForm中,按钮就是按钮。在WPF中按钮可以任何部件。
传统的事件
在这里插入图片描述
路由事件
在这里插入图片描述

识别路由事件

路由信息主要包括唯一标识ClickEvent,事件委托形式RoutedEventHandler,以及路由策略,Click事件的路由策略是Bubbling.

路由事件的旅行

路由事件由事件源作为起点,每个事件监听者都要路过。
路由事件的策略有3种

  • Bubbling: 事件从事件源触发一路上追溯到根节点。
  • Direct:事件从事件源出发,围绕事件源转一圈结束。
  • Tunneling:事件源触发事件后,事件从根节点下沉直到事件源。
    改变旅行策略因素之一:事件处理函数
    在这里插入图片描述
    改变旅行策略因素之二:类和实例事件处理函数
    事件处理函数有两种类型:一是前面所说的普通事件处理函数,称为“实例事件处理函数”(Instance Handlers);二是通过EventManager.RegisterClassHandle方法将一个事件处理函数和一个类关联起来,称为“类事件处理函数”(Class Handlers),其优先级高于前者。

WPF中Command

使用Command的主要原因:

  • 使代码更为简洁,容易支持菜单,工具栏和快捷键等多种途径触发。
  • 无须担心各个UI的状态(启用或者禁用)
    在这里插入图片描述

WPF的Command模型

模型包括四个部分:

  • Command:应用程序需要执行的任务,比如关闭等任务。
  • CommandBinding: 连接Command和特定的应用程序逻辑。
  • CommandSource:触发Command的对象。
  • Command target :Command 应用在上面的对象。
    Command

ICommand接口

Execute方法:当Command被触发时调用该方法。执行与命令相对应的操作。
CanExecute方法:用来判断该命令是否可应用到当前Command target上,如果该方法返回为true,则可以;否则不行。
CanExceuteChanged事件:Command有执行或者不执行两种状态,状态改变时触发该事件,一般监听该事件的是
CommandSource,他监听到该事件会调用绑定的Command的CanExecute方法检查当前Command的状态,然后决定CommandSource 是否启用.

类RoutedCommand

WPF内置的Command库

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值