zoukankan      html  css  js  c++  java
  • XAML UserControl的继承

    欢迎访问Heroius博客:崩溃的脑壳查看文章原文!

    前言

    相信不少学习WPF和Silverlight的同学们都出于Winform的习惯,希望能够在新展示层框架中实现控件的继承。本文就是说明如何实现这一点。

    但是在正文开始之前,必须要指明,一般情况下,在WPF/SL中并不推荐使用自定义控件或控件继承(当然,使用模板生成的Window, UserControl, Page不在此限),因为基于XAML的前台设计语言本身具有丰富的表现能力,且框架支持样式(Style)、模板(Template)等控制外观和行为的方式—-这些特性足以构建出任何形式的界面UI。反过来说,创建自定义控件或实现控件继承在WPF/SL中不仅不受推崇,其实现难度也要比Winform中大。

    那么当什么时候才不得不使用自定义控件呢?就是这个控件需要在多处实例化使用,而本身内容较为复杂时。相比较而言,控件的继承使用的情况就更少了,它使用的必要性一般要满足如下几条:

    1. 只在原控件不满足新需求,但同时又有可资利用的价值;
    2. 新控件需要直接访问原控件成员(否则在新控件中包含原控件即可,不必继承);
    3. 新控件同样需要在多处使用。

    如果你面临的情况满足这几个条件,阅读本文可能会提供帮助。

    问题分析

    在WPF/SL中实现控件继承之所以会比Winform困难,是因为在底层框架设计上WPF/SL将表示层彻底从逻辑中分离了出去,控件外观几乎均有XAML标记定义,而在对控件进行继承时,XAML部分对应的类型成员是无法被继承的。

    以WPF为例,使用UserControl模板新建用户控件,得到一个.cs文件和一个.xaml文件。注意在.cs文件中类定义有partial修饰,说明是分部类,代码中的InitializeComponent函数即是在另外一部分代码(设计器生成代码)中定义的。这部分由设计器生成的代码和Winform中设计器的代码相差很大。

    partial-300x172 XAML UserControl的继承

    在生成文件夹中可以看到设计器生成的.g.i.cs文件,其中包含对应于XAML内命名成员的相应变量的定义,以及InitializeComponent方法实现。

    gics-300x263 XAML UserControl的继承

    可见于Winform设计器代码相比,其中不包含C#代码形式的控件初始化逻辑,所有界面表达均在xaml文件中,代码通过System.Windows.Application.LoadComponent方法从xaml文件实例化。

    在.xaml文件中,可见<UserControl>标签及其属性x:class,此两者指明了XAML文档对应的类型信息,其中根元素是当前类型的基类(UserControl),x:class属性指定当前类型(UserControl1)。

    uc1-300x19 XAML UserControl的继承

    注意到Application.LoadComponent方法包含两个重载:

    1. object (Uri) – 接受xaml资源的定位符,返回其根元素决定的实例;
    2. void (object, Uri) – 接受根元素类型实例和资源定位符。

    自动生成代码采用了第2个重载,并传递当前类实例作为第一个参数,也就是说,XAML加载得到了拓展的UserControl1类型。

    这种在xaml中指定类型信息的类(UserControl1)被称为是“由XAML定义的”。而XAML渲染器无法识别由XAML定义的根类型,也就是说当控件继承时,若在子类型控件(如UserControl2)设计器中指定其为根元素时,编译过程将失败。

    但假如子类型不包含XAML代码,如新建Class1继承自UserControl1,则没有问题。

    cl1 XAML UserControl的继承

    现在的问题是,在设计控件,尤其是结构较复杂时,我们往往需要借助设计器,这就要求必须使用XAML代码,这种情况应该如何应对呢?

    XAML UserControl的继承

    命题:两个带有设计界面的类型UserControl1和UserControl2,其中后者继承于前者。

    思路:

    1. 对于xaml代码,利用Application.LoadComponent方法可获取根元素决定的实例;
    2. UserControl属于Content Control,其显示内容由Content决定;
    3. 基于以上两点,分离控件xaml部分,并修改为以某FrameworkElement为根,如此可得到用于设置Content属性的可视化内容。

    UserControl2继承代码修改

    修改UserControl2的代码,使其继承自UserControl1

    修改xaml代码,将根元素设置为基类,注意引用本地程序集命名空间

     UserControl1 xaml和代码分离

    UserControl1控件包含的.xaml和.xaml.cs文件由VS管理,为了使两者分开,需要重命名。先将项从项目中移除,分别重命名:

    1. UserControl1.xaml -> UserControl1_skin.xaml
    2. UserControl1.xaml.cs -> UserControl1.cs

    重新加载到项目中,修改xaml文件根元素,使用Grid代替UserControl

    移除UserControl.cs中类定义前的partial修饰符,并手动添加InitializeComponent函数,在其中利用Application.LoadComponent的第一个重载获取如上修改之后xaml编译得到的Grid实例,将其设置给Content:

     UserControl2的代码调整

    为避免UserControl2自动生成代码覆盖基类的Content内容,在调用UserControl2的InitializeComponent函数之前需要获取基类Content,即上文中的Grid实例,并将其插入到当前UserControl2的最下层。

    此时在主窗体中拖放UserControl2,程序运行效果如下:

    run-300x200 XAML UserControl的继承

    其他注意事项

    关于Silverlight

    Silverlight中不包含 Application.LoadComponent的第一个重载,可事先创建Grid实例,之后将其作为参数调用 Application.LoadComponent第二重载,效果和WPF一样。

    关于界面设计

    若UserControl1界面中有交互内容,设计UserControl2时需要注意避让。

    实际上,完全可以通过代码控制界面元素的布局,例如可以尝试将UserControl2构造函数的代码改成如下内容:

     示例代码

    访问 崩溃的脑壳 文章页面底部以获取百度网盘地址和提取密码

  • 相关阅读:
    解释机器学习模型的一些方法(一)——数据可视化
    机器学习模型解释工具-Lime
    Hive SQL 语法学习与实践
    LeetCode 198. 打家劫舍(House Robber)LeetCode 213. 打家劫舍 II(House Robber II)
    LeetCode 148. 排序链表(Sort List)
    LeetCode 18. 四数之和(4Sum)
    LeetCode 12. 整数转罗马数字(Integer to Roman)
    LeetCode 31. 下一个排列(Next Permutation)
    LeetCode 168. Excel表列名称(Excel Sheet Column Title)
    论FPGA建模,与面向对象编程的相似性
  • 原文地址:https://www.cnblogs.com/heroius/p/4003639.html
Copyright © 2011-2022 走看看