原文:《Programming WPF》翻译 第9章 4.模板

对一个自定义元素最后的设计考虑是,它是如何连接其可视化的。如果一个元素直接从FrameworkElement中派生,这将会适当的生成它自己的可视化。(第7章描述了如何创建一个图形外观。)尤其是,如果你创建了一个元素,是为了提供一个特定的可视化表现,该元素应该完全控制这个可视化是如何管理的,一旦你编写了一个控件,通常你不会将一个图形硬编码到里面。

记住,一个控件的工作是提供行为。可视化是由控件模板提供的。这种可视化是由控件模板提供的。一个控件可能提供一组默认的可视化,而应允许这些可视化被替换,为了提供像内迁控件一样的弹性。(第五章描述了如何使用模板替换一个控件的可视化)符合这种方法的控件,这里可视化从控件中分离出来,通常引用到一个没有外观的控件。所有内迁到WPF的控件都是没有外观的。

当然,控件完全独立于其可视化是不可能的。任何控件将对模板必须满足的需求施加影响,如果控件操作正确。这些需求的程度随控件不同而不同。例如,Button有一个相当简单的需求——仅仅需要一个占位符放置标题或内容。Slider控件需要更广泛的需求:可视化必须提供两个按钮(增加和减少),“Thumb”,以及运行时Thumb上的一个跟踪。此外,它还需要能够响应点击和拖动在这些元素的任意一个,以及能够定位这个Thumb。

在任意控件类型和样式或模板之间有一个隐式的约定。这个控件允许它的外观通过替换可视化树的方式进行自定义,但是这棵树必须轮流提供代表这棵树的某些特征。这个约定的本性依赖于这个控件,内嵌控件使用一些不同的样式,紧紧依赖于它们的可视化结构。下面的部分描述了很多将控件与其模板联系在一起的方式

9.4.1属性别名

控件和模板间最松散的约定形式是控件简单的定义了公有属性,以及允许模板来决定哪一个属性在别名中可见。(参见第5章获取更多属性别名的信息。)这个控件并不关心

在控件中是什么。

这里有一个单行的约定:控件提供属性和命令,不需要返回值。尽管如此,如果必要的话,这样的控件仍能响应用户输入。事件路由允许事件从可视化向上冒泡到控件。控件能够处理这些事件而不需要知道任何关于可视化本性的信息。

为了支持这个模型,你所要做的是,使用本章先前描述的依赖属性机制,来实现这些属性。示例9-11显示了一个自定义控件,并且定义了一个单独的名为Foo的依赖属性,Brush类型。

依赖属性支持这个控件的用户在模板中提及,正如示例9-12所示。

示例9-12

<ControlTemplate TargetType="{x:Type local:MyCustomControl}">

    <Grid>

        <Rectangle Fill="{TemplateBinding Foo}" />

    </Grid>

</ControlTemplate>

所有的依赖属性自动支持属性别名。这种情形下的“约定”是由一组你的控件提供的依赖属性暗示的。

9.4.2占位符

一些控件希望在模板中找到一个特定的占位符元素。这将要么采取该元素指定类型的形式,或者可以是一个元素标记了一个特定的属性。

控件通过派生于ContentControl支持内容模板,使用元素类型的方法。它们希望在模板中找到一个ContentPresenter元素。这是一个特殊意图的元素,它的工作是在其他内容中担当一个占位符。

实际上,这是一个松散的强迫性的约定。如果模板中没有ContentPresenter,ContentControl通常不会申诉。控件并不绝对依赖于表现的内容,为了放在那里起作用。或者你能到达另一个极端,以及放一些ContentPresenter在你的模板中,可以使子内容多次出现。

你不需要做任何特殊的事情来支持ContentPresenter的使用,只要你派生于ContentControl,它可以很好的工作。控件的用户能够编写一个模板,正如示例9-13所示。

示例9-13

<ControlTemplate TargetType="{x:Type local:MyContentControl}">

    <Grid>

        <Rectangle Fill="White" />

        <ContentPresenter />

    </Grid>

</ControlTemplate>

9.4.3通过属性指定占位符

一些控件寻找用一个特定属性标记的元素。例如,派生于ItemsControl的控件,如ListBox和MenuItem,希望模板包括一个带有Panel.IsItemsHost属性设为true的元素。这标志了Panel将要扮演控件数据项目的宿主。ItemCOntrol使用附属属性取代占位符的原因是,允许你决定使用什么类型的Panel,作为数据项的宿主。(ItemControl还支持ItemsPresenter占位符元素的使用。这将使用于当样式不希望利用特定的panel类型的时候以及想要使用无论控件的默认panel是什么的时候)

为了实现使用此技术的控件,你需要定义一个自定义附属依赖属性,将其应用到占位符。这是一个Boolean属性。示例9-14注册了这样一个附属属性,并定义了通常的访问器功能。

示例9-14

public class ControlWithPlaceholder : Control {

    public static DependencyProperty IsMyPlaceholderProperty;



    static ControlWithPlaceholder( ) {



        PropertyMetadata 

 isMyPlaceholderMetadata = new PropertyMetadata(false,

            new PropertyInvalidatedCallback 

(OnIsMyPlaceholderChanged));



        IsMyPlaceholderProperty = DependencyProperty.RegisterAttached(

            "IsMyPlaceholder", typeof(bool),

            typeof(ControlWithPlaceholder), isMyPlaceholderMetadata);

    }



    public static bool GetIsMyPlaceholder(DependencyObject target) {

        return (bool) target.GetValue(IsMyPlaceholderProperty);

    }

    public static void SetIsMyPlaceholder(DependencyObject target, bool value) {

        target.SetValue(IsMyPlaceholderProperty, value);

    }



注意到示例9-14为PropertyMetaData提供了一个PropertyInvalidatedCallBack。这指示了一个可以在任意时间调用的方法,这个附属属性可以在任意元素上被设置或修改。在这种方法中,我们的控件将发现哪个元素被设置为占位符,示例9-15显示了这个方法。

示例9-15



    private static void OnIsMyPlaceholderChanged(DependencyObject target) {



        FrameworkElement targetElement = target as FrameworkElement;

        if (targetElement != null && GetIsMyPlaceholder(targetElement)) {

            ControlWithPlaceholder containingControl =

                targetElement.TemplatedParent as ControlWithPlaceholder;

            if (containingControl != null) {

                containingControl.placeholder = targetElement;

            }

        }

    }

    private FrameworkElement placeholder;



    

}

这个示例开始于检测属性被应用到派生于FrameworkElement的对象。记住我们希望这个属性会被应用到一个特定的控件模板内的UI元素,因此如果被应用到别的元素而不是FrameworkElement,我们这么做就得不到什么有用的东西。

其次,我们通过GetIsMyPlaceholder访问器方法检测了属性值,该方法是我们在示例9-14为附属属性定义的。这将是些微单独的,如果有人显示的设置这个属性为false,但是如果确实是这样,我们干脆不应该把元素作为占位符。

如果这个属性设置为true,我们继续获取目标元素的TemplatedParent属性。因为元素作为控件的模板一部分,这将返回可视化所属于的控件。(如果这个元素不是控件的成员,那么返回null。既然这个属性仅仅对模板中的元素有意义,如果没有模板化的父一级,我们就做不了任何事情。)我们还检查了父一级是一个控件类型的一个实例,而且忽略了属性,如果被应用到一个模板中的元素,在某种其它类型的控件模板中。

示例9-16显示了如何在一个控件模板中使用属性,来表明是哪一个元素在占位符中。

示例9-16

<ControlTemplate TargetType="{x:Type local:ControlWithPlaceholder}">

    <Grid local:ControlWithPlaceholder.IsMyPlaceholder="true" />

</ControlTemplate>

一些控件希望有一种模板,提供一组详细明确的元素,来履行特定的角色在控件的标签中。例如,HorizontalSlider控件希望模板包含表示可拖动thumb的元素,这个可点击的跟踪,在thumb的任意一边,等等。模板需要指出哪一个元素是哪一个。这可以通过使用上述显示的技术,定义多个附属属性来实现。

当你写一个使用了占位符的控件时,你可能选择不执行这个约定。例如,如果模板的任意部分不见了,slider控件不会抱怨。一旦你只提供了要寻找的一些元素,这可以工作而不用抱怨。

《Programming WPF》翻译 第9章 4.模板的更多相关文章

  1. 《Programming WPF》翻译 第5章 7.控件模板

    原文:<Programming WPF>翻译 第5章 7.控件模板 如果仔细的看我们当前的TTT游戏,会发现Button对象并没有完全为我们工作.哪些TTT面板有内圆角? 图5-14 这里 ...

  2. 《Programming WPF》翻译 第5章 5.数据模板和样式

    原文:<Programming WPF>翻译 第5章 5.数据模板和样式 让我们想象一下我们想要实现TTT更有娱乐性的一个版本(这是大部分游戏中最重要的特色).例如,TTT的一种变体允许玩 ...

  3. 《Programming WPF》翻译 第9章 5.默认可视化

    原文:<Programming WPF>翻译 第9章 5.默认可视化 虽然为控件提供一个自定义外观的能力是有用的,开发者应该能够使用一个控件而不用必须提供自定义可视化.这个控件应该正好工作 ...

  4. 《Programming WPF》翻译 第9章 6.我们进行到哪里了?

    原文:<Programming WPF>翻译 第9章 6.我们进行到哪里了? 只有当任何内嵌控件都没有提供你需要的底层行为时,你将要写一个自定义控件.当你写一个自定义控件,你将要使用到依赖 ...

  5. 《Programming WPF》翻译 第9章 2.选择一个基类

    原文:<Programming WPF>翻译 第9章 2.选择一个基类 WPF提供了很多类,当创建一个自定义元素时,你可以从这些类中派生.图9-1显示了一组可能作为类--可能是合适的基类, ...

  6. 《Programming WPF》翻译 第9章 1.自定义控件基础

    原文:<Programming WPF>翻译 第9章 1.自定义控件基础 在写一个自定义控件之前,你需要问的第一个问题是,我真的需要一个自定义控件吗?一个写自定义控件的主要原因是为了用户界 ...

  7. 《Programming WPF》翻译 第8章 6.我们进行到哪里了?

    原文:<Programming WPF>翻译 第8章 6.我们进行到哪里了? 动画可以增强应用程序的交互感.它有利于更平滑的转换--当条目出现或消失的时候.它应该,当然,被用于体验和重新着 ...

  8. 《Programming WPF》翻译 第8章 3.Storyboard

    原文:<Programming WPF>翻译 第8章 3.Storyboard Storyboard是动画的集合.如果你使用了标记,所有的动画必须要被定义在一个Storyboard中.(在 ...

  9. 《Programming WPF》翻译 第6章 5.我们进行到哪里了?

    原文:<Programming WPF>翻译 第6章 5.我们进行到哪里了? WPF提供了资源工具,让我们运用在用户界面中,动态并具有一致性.我们可以在资源字典中存储任意资源,并且可以遍及 ...

随机推荐

  1. 动态Linq(结合反射)

    这篇文章决定对最近一个单机版Web程序用到的东西总结一下. 一.反射Linq之OrderBy 动态Linq结合反射对某字段排序: namespace 动态Linq { class Program { ...

  2. NOI2013 UOJ122 向量内积

    神题...... 还是大神讲得比较清晰~orz http://dffxtz.logdown.com/posts/197950-noi2013-vector-inner-product 启发题:poj3 ...

  3. 关于bootstrap--列表(ol、ul)

    1.list-unstyled : 在<ol>(有序列表)</ol><ul>(无序列表)</ul>中加入class="list-styled& ...

  4. Swift字符串常用操作总结

    转自:http://www.jianshu.com/p/52e7580166ff 1.string转换为Int/Long/Float/Double/Bool等 var str1="100&q ...

  5. python3-day6(模块)

    一.OS模块 1.os.system('ls -l') #子shell运行,获取返回值,不是结果. 2.os.popen('ls -l').read() #获取结果. 二.sys模块 1.sys.ar ...

  6. python下载多个文件

    # -*- coding: utf-8 -*-__author__ = 'Administrator'import urllib2,urllib,os,redef Url1(url):#多个文件    ...

  7. 企业生产环境中linux系统分区的几种方案

    方案1:针对网站集群架构中的某个节点服务器分区 该服务器上的数据有多份(其他节点也有)且数据不太重要,建议分区方案如下: /boot: 200MB swap: 物理内存的1.5倍,当内存大于或等于8G ...

  8. APP常用模块

    2016年上半年 APICloud合作云服务商提供了各种类型模块多达45个 其中最新发布的重要模块有 美洽客服模块 亲加视频直播相关模块 保利威视视频播放器模块 苹果银联支付模块 贝宝支付模块 谷歌分 ...

  9. 字符串匹配之horspool算法(简化的BM算法)

    前面介绍在BF,KMP这些算法的时候老是提到BM这个东西,究竟这什么东西,有啥高深的,这些问题我们如今不去考虑.不知道,认真读前几篇文章的读者有没有发现前面的算法都是从模式串的前面開始匹配的,那我们就 ...

  10. Gson源码分析之Json结构抽象和注解使用

    github上的博客地址: http://chuyun923.github.io/blog/2015/01/06/gsonyuan-ma-fen-xi/ XML和Json作为最常用的两种网络传输格式而 ...