zoukankan      html  css  js  c++  java
  • 水印管理器:3、设计时支持

    (偷完菜,喝口水,继续写文章)

    怎么,觉得编写冗余的代码太麻烦?没关系,让设计器为你完成,点点鼠标,完成大部分工作。

    我一直是这样认为的,一个组件要做得完善,良好的设计时的支持是不可缺少的一部分,不但能让最终用户体会到功能的强大,还能让程序员在开发的过程中感受到方便!不过这是需要付出代价的,因为设计时支持的开发有时候会比组件本身更加麻烦,因为设计器的行为有时会比较刻板,我们所做的工作必须符合它的要求。幸好Cuer这个组件的设计时支持的功能并不算太多,实现起来也比较容易,如果你正好在为这样的功能烦恼,希望能给你一些帮助。

    Cuer组件本身没有什么需要设计的地方,但由于它需要在控件外部管理控件的行为,就需要对控件进行扩展。最初想到的就是ToolTip这个组件,它可以在不修改控件的情况下为窗体上的每个控件设置一个提示信息,而在设计时可以发现,当窗体中放入一个ToolTip组件时(假设名称为toolTip1),每个控件的属性窗口中都会多出一个属性“toolTip1上的ToolTip”。这个正是Cuer要的效果,先来看看实现以后的实际情况吧,截图来自源码中的演示窗体的属性编辑器。
     
    查看MSDN里ToolTip的声明,发现它原来实现了IExtenderProvider接口,IExtenderProvider接口可以在设计器中将属性扩展到其它组件,它唯一的一个成员是CanExtend方法,用于检查指定的对象是否允许被扩展。另外,还需要配合ProvidePropertyAttribute,它用来告诉设计器,哪个属性是用来扩展的。还是看代码吧,简单的几行就搞定了。

        [ProvideProperty("Cue"typeof(Control))]
        
    public class Cuer : Component, IExtenderProvider
        {
            
    bool IExtenderProvider.CanExtend(object extendee)
            {
                
    return IsSupported(extendee as Control);
            }
        }

    ProvidePropertyAttribute的构造函数需要提供两个参数,第一个是属性名称,也就是当前组件的哪个属性用于扩展;第二个参数是可选的,表示需要对哪个类型的的对象进行扩展。关于第一个参数,需要说明的是,所给定的名称并不是代表真正的属性,而是需要用来匹配一对方法,对于以上代码,需要匹配的是GetCue和SetCue,这两个方法在Cuer中已经有了,因此可以直接使用。而在IExtenderProvider.CanExtend的实现中,也只要返回IsSupported方法的值即可,这些都可以说是现成的。 

    是否这样就完成了呢?实际使用后发现了问题,多出来的属性是有了,显示为“cuer1上的Cue”,但怎么也输不进内容,这是怎么回事呢。呵呵,很简单,因为SetCue方法第二个参数的类型是CueInfo,而不是String,而属性编辑器只能输入字符串,并且它不知道应该怎么把String转换为CueInfo,因此就不让输入了。这时,就该类型转换器上场了。我敢保证,不管有没有开发过组件,只要用过Visual Studio的设计器,就肯定受到过类型转换器的恩惠。在属性编辑器里,把输入的文字转换为相应的类型都需要类型转换器,比如象Location、Size等这些比较明显的转换。那么,CueInfo的类型转换器要怎么做呢,先看图。

     

    TypeConverter是所有类型转换器的基类,所以CueInfoConverter也必须继承它,并且重载几个必要的方法来实现编辑器里String到CueInfo的转换,这些方法都是成对出现的,分别简要说明一下。

    CanConvertTo和ConvertTo,前者是用来测试是否能将当前类型(CueInfo)转换为指定的类型,因为属性编辑器需要的类型是String,因此只要目标类型是String就可以转换了。后者就是进行实际的转换并返回转换后的结果,当然也仅针对String。对于String以外的类型,就交给基类来完成。参考以下代码:

        if (destinationType == typeof(string))
        {
            
    if (value != null)
            {
                StringBuilder builder 
    = new StringBuilder();
                CueInfo cue 
    = (CueInfo)value;

                builder.Append(cue.Text);
                
    if (!cue.HideOnFocus) builder.Append(", NH");

                
    return builder.ToString();
            }
            
    else return string.Empty;
        }

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

    从代码里不难看出,返回的字符串就是水印的文本,并且当HideOnFocus设置为false时,会在后面加个“, NH”的标记来表示(因为true是默认值),这个标志会由后面的ConvertFrom进行解析。如果设置的显示文本为“User name”,并且设置HideOnFocus为false,那么在属性编辑器里就会显示为“User name, NH”。

    CanConvertFrom和ConvertFrom正好是反向操作,通过解析将String转换为CueInfo,如果发现最后有“, NH”标记,就从字符串里移除这个标记,并且设置CueInfo的HideOnFocus属性为false。代码就不帖了,自己去源代码里找。

    GetPropertiesSupported和GetProperties这对方法是告诉属性编辑器是否支持属性列表。这是什么东西呢?复习一下刚才的截图:
     
    那两个缩进一点的属性就是属性列表返回的内容。在GetProperties方法里,可以通过TypeDescriptor.GetProperties方法返回所有属性,如果需要对属性进行排序,还可以在返回结果上调用Sort方法。假如GetPropertiesSupported返回的值是false,那在属性编辑器里就不会显示出加号了。如果想要某个属性不显示出来,只要为这个属性加上BrowsableAttribute的特性,并在构造函数中指定参数为false即可,就象图中没有显示Manager和Control这两个属性。

    GetCreateInstanceSupported和CreateInstance这两个方法一个是用来确认是否在CueInfo的属性更改了以后创建新的对象,另一个是用来创建对象的新实例。这对方法在转换结构(struct)时是非常有用的,在这里由于每次传入SetCue的对象都必须是新的对象,因此也必须CreateInstance。CreateInstance方法提供了一个存储各个属性值的字典对象,这些属性就是显示在属性编辑器里加号后面的各个属性,直接取出来赋值到相应的属性上即可。

    做完这些,再看看属性编辑器吧,是不是有了截图上的效果呢,呵呵。接下来,就是点点鼠标,输入几个必要的参数,如果没有特殊的要求,根本不用写一行代码,懒人有福咯。

    对于Cuer组件的说明至此已经全部完成,稍稍总结一下:Cuer组件的功能是管理窗体上控件的水印,它的核心实现原理就是向所管理的控件通过SendMessage这个API函数发送EM_SETCUEBANNER消息。虽然核心很简单,但是要做到“管理”还是花费了一定的功夫,一方面要将控件“管”起来,另一方面还要提供设计时的支持。虽然这个功能并不一定有非常大的需求,但通过这个组件的开发,可以学习到一些WinForm编程的知识,为将来开发更复杂的组件提供必要的知识积累。

    通过Win32实现的水印管理器已经完成,先前我说到过,这种方式有一定的限制,首先是操作系统版本的限制,只有XP或更新的版本才能支持,其次是功能的限制,无法实现诸如文字位置、颜色、甚至图片等更多的需求。如果想要做这些,还得靠自己动手DIY。

    To be continued...

  • 相关阅读:
    Redis学习
    extractor
    Linux fork exec等
    Linux kill 命令
    GCC参数使用
    Shell 参数(2) --解析命令行参数工具:getopts/getopt
    Shell 参数(1)
    shell 中并发执行
    Linux 下新增用户的流程
    Linux 安全rm
  • 原文地址:https://www.cnblogs.com/effun/p/1551768.html
Copyright © 2011-2022 走看看