yet

Dream Will Come True

 

自定义提供程序控件(续)

让我们增强这个简单的 TextBox 扩展程序,以便为每个 extendee 提供更好的支持。扩展程序现在将添加两个属性 — 背景色和前景色。
[ProvideProperty("SelectedBackColor", typeof(TextBox))]
[ProvideProperty("SelectedForeColor", typeof(TextBox))]
public class TextBoxExtender : Component, IExtenderProvider
{
   private Hashtable Extendees;

   public TextBoxExtender()
   {
       InitializeComponent();

       // Use a hashtable to track selected colors
       // for each extendee
       this.Extendees = new Hashtable();
   }

   •••
}

每个 extendee 控件都被分配给按如下方式构造的信息类:

public class TextBoxInfo
{
   public Color SelectedBackColor;
   public Color OldBackColor;
   public Color SelectedForeColor;
   public Color OldForeColor;
   public bool  EventsWired;
}

该类不但跟踪要使用的颜色,而且跟踪要恢复的颜色,并对 GotFocus 和 LostFocus 事件仅绑定一次,这是由 EventsWired 属性指示的。Get/Set 对的代码变得有点复杂。让我们先处理 Get 访问器:

public Color GetSelectedBackColor(Control control)
{
   // Retrieve related info
   TextBox t = (TextBox) control;
   TextBoxInfo info = (TextBoxInfo) Extendees[t];

   return info.SelectedBackColor; 
}

控件参数引用实际的 extendee 控件 — 在本例中为 TextBox。在获得了对基础控件的引用之后,还可以基于可访问的名称或任何其他属性筛出控件。将控件引用用作访问哈希表和检索相应 TextBoxInfo 结构的关键字。在此之后,返回要使用的背景色易如反掌。

谁正在哈希表中创建条目,以及何时创建?其中一个属性的 Set 访问器负责在哈希表中插入控件。如果类似的条目已经存在于哈希表中,将不会添加控件。下面显示了 SetSelectedBackColor 方法的源代码。
public void SetSelectedBackColor(Control control, Color
    selColor)
{
    TextBoxInfo info;
    TextBox t = (TextBox) control;
    if (!Extendees.ContainsKey(t))
        info = new TextBoxInfo();
    else
        info = (TextBoxInfo) Extendees[t];

    // Store the new value
    info.SelectedBackColor = selColor;

    // If not already done, wire events up
    if (!info.EventWired)
    {
        t.GotFocus += new EventHandler(TextBox_GotFocus);
        t.LostFocus += new EventHandler(TextBox_LostFocus);
        info.EventWired = true;
    }

    // Add to the table
    if (!Extendees.ContainsKey(t))
        Extendees[t] = info;
}

ToolTip 类被密封并且不能被继承,因此不能添加更多的功能(如气球样式)。在 Windows XP 和更新的操作系统中,ToolTip 窗口已被赋予 TTS_BALLOON 样式,以便在气球样式弹出窗口中显示标题。首先,从现有的 ToolTip 提供程序派生新扩展程序类并为气球样式添加支持,这看上去是相对简单的任务。您遇到的第一个问题就是 ToolTip 类是密封类,即它不再可继承。但是,通过使用聚合(而非继承),您可以设计自定义包装扩展程序,以便利用 ToolTip 类的基础功能。

其思路在于在扩展程序的构造函数中创建 ToolTip 类的新实例,并在新的 ToolTip 属性的 get 和 set 访问器中调用 GetToolTip 和 SetToolTip 方法。

[ProvideProperty("MyText", typeof(TextBox))]
public class MyToolTip : Component, IExtenderProvider
{
   private ToolTip _toolTip;
   public MyToolTip() 
   {
      toolTip = new ToolTip();
   }
   
}

MyToolTip 类嵌入 ToolTip 类的动态创建的实例作为其私有属性,并公开 MyText 扩展属性。此属性的实现通过针对初始 ToolTip 类实现文本属性来传递:

public string GetMyText(Control control)
{
   return _toolTip.GetToolTip(control); 
}

public void SetMyText(Control control, string caption)
{
   // TODO :: Enter your changes...

   _toolTip.SetToolTip(control, caption);
}
扩展 ToolTip 扩展程序

通过实现此代码,可以完成自己的自定义扩展程序,并使其在现有 ToolTip 组件的上方工作。扩展 ToolTip 的一个很好的理由是使用自定义的 ToolTip 控件。Windows 窗体 ToolTip 不支持气球和多行样式,只能通过自定义 ToolTip 扩展程序添加这些功能。不幸的是,在三个内置扩展程序中,您需要的扩展程序(即 ToolTip 组件)是密封的,并且不能被继承。因此,您必须采用基于控件聚合的方法。

然而,在实现完全自定义 Windows 窗体 ToolTip 的过程中,主要的障碍却不在这里。ToolTip 控件从 Component(而非 Control)继承。对于哪种方式更好这一问题存在一些争论。如果您从 Component 继承某个类,该类的行为方式类似于控件栏组件,这对于扩展程序非常适合。如果从 Control 派生该类,结果组件无法放在 IDE 栏区域中。然而,它将从基类继承 Handle 属性。

Handle 属性表示基础 Win32 控件的 HWND句柄。由于 Windows 窗体在很大程度上基于 Win32 控件,因此当 Control 对象被实例化时,Windows 窗体会创建一个窗口。要自定义控件的外观和行为,您需要采用 Win32 样式和消息。如果您不能访问基础窗口句柄,则如何设置它们?

ToolTip 托管控件确实有一个被定义为 IntPtr 成员的 Handle 属性。回忆一下,IntPtr 是用来映射 Win32 句柄(如 HWND、HGLOBAL 或 HKEY)的 .NET Framework 类型。困难在于如下事实,ToolTip 的句柄属性被声明为私有属性,所以会因其保护级别而变得不可访问。ToolTip 类可以使用聚合功能进一步扩展,但是,能够实际添加的扩展仅限于在不知道基础 ToolTip 窗口的 HWND 的情况下所能实现的任何事情。

以上源代码下载地址: CuttingEdge0311.exe (139KB)

posted on 2005-07-20 21:56  yet  阅读(451)  评论(0编辑  收藏  举报

导航