65.9K
CodeProject 正在变化。 阅读更多。
Home

HintTextBox

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.19/5 (12投票s)

2007年5月15日

CPOL

3分钟阅读

viewsIcon

51662

downloadIcon

683

为文本框显示内联提示。适用于 UI 较小且需要容纳大量输入控件的情况。

Screenshot - HintTextBox_Form.pngScreenshot - HintTextBox_Form_Password.png

引言

我们(UI 设计师)经常面临需要在单个屏幕上捕获大量用户信息的场景。在这种情况下,由于空间限制和窗体尺寸,似乎无法在单个窗体中添加足够的标签和文本框。如今,我们看到许多网站上的窗体都解决了这个问题。其理念是“为什么不将标签添加到文本框本身呢?”也就是说,文本框本身能否同时充当标签(标题)并捕获用户输入?

本文尝试使用一个我已经在许多商业应用程序中使用过的自定义控件来解决这类问题。此外,它还是一个开箱即用的控件,并带有一个自定义 UI 智能标签设计器,非常方便。

背景

在我的一款应用程序中,窗体上只剩下足够的空间来容纳两个文本框。我当时想,为什么不模仿 Internet 网页表单的方式,添加一个既能充当文本框又能充当标签的文本框呢?

我快速谷歌了一下,找到了一些类似的控件,它们解决了我的问题。嗯,看起来是这样。我使用的控件功能如下:

  1. 将一个 Label 控件嵌入到 TextBox 中。
  2. Label 获得焦点和失去焦点时切换其可见性。

这似乎是一个很棒的技巧,但促使我编写自己的控件的原因是:在上帝的份上,既然一个字符串就能完美地完成这项工作,为什么还要使用 Label 呢?

我设法完成了应用程序并交付了它,但开发此类控件的愿望始终在我脑海中盘旋。于是,我留下了一个充满好奇心的大脑,以及开发一个可以在所有应用程序中使用的控件的需要,而且还要带有一个智能标签设计器,以便快速完成 UI 的准备工作。

控件代码

好吧,控件代码比看起来要简单得多,尽管有点长。您可以在上面的 Zip 文件中浏览源代码。

想法

  • 添加一个类型为 string 的属性,并在获得和失去焦点时用它与基类 TextBoxText 属性进行交换。
  • 使用一些交换变量来保存 ForecolorText 属性的值。

智能标签设计器

这配置起来有点难,但经过一些初步的困难后,我发现智能标签依赖于智能标签 UI 设计器,而智能标签 UI 设计器继承自System.Design.dll 程序集中的 System.Windows.Forms.Design.ControlDesigner 类。

每个继承的类都应该有一个 DesignerActionListCollection 成员变量,它保存对一个类的引用,该类包含应在智能标签中显示的属性。

实际包含要显示属性的类是继承自 DesignerActionList 类的类。

总之,这有点棘手……下面是代码

/////////////////////////////////////////////////////////////////
// Designer for the HintTextBox control with support for a smart 
// tag panel.
// Must add reference to System.Design.dll
/////////////////////////////////////////////////////////////////
[System.Security.Permissions.PermissionSet(
        System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class ColorLabelDesigner : System.Windows.Forms.Design.ControlDesigner
{
    private DesignerActionListCollection actionLists;

    // Use pull model to populate smart tag menu.
    public override DesignerActionListCollection ActionLists
    {
        get
        {
            if (null == actionLists)
            {
                actionLists = new DesignerActionListCollection();
                actionLists.Add(new HintTextBoxDesignerActionList (this.Component));
            }
            return actionLists;
        }
    }
}

//the above class (ColorLabelDesigner )acts as a connector 
//connecting the Control and its Smart Tag Designer UI

internal class HintTextBoxDesignerActionList : DesignerActionList
{
    HintTextBox _BaseControl;
    private DesignerActionUIService designerActionUISvc = null;

    public HintTextBoxDesignerActionList(IComponent component)
        : base(component)
    {
        try
        {
            _BaseControl = (HintTextBox)component;
            this.designerActionUISvc = 
              GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    // Helper method to retrieve control properties. Use of 
    // GetProperties enables undo and menu updates to work properly.
    private PropertyDescriptor GetPropertyByName(String propName)
    {
        PropertyDescriptor prop;
        prop = TypeDescriptor.GetProperties(_BaseControl)[propName];
        if (null == prop)
            throw new ArgumentException(
                 "Matching property not found!",
                  propName);
        else
            return prop;
    }

    public bool IsPassword
    {
        get { return _BaseControl.IsPassword; }
        set
        {
            GetPropertyByName("IsPassword").SetValue(_BaseControl, value);
            _BaseControl.Multiline = false;
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public bool MultiLine
    {
        get { return _BaseControl.Multiline; }
        set
        {
            GetPropertyByName("Multiline").SetValue(_BaseControl, value);
            _BaseControl.IsPassword = false;
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public string Hint
    {
        get { return _BaseControl.Hint; }
        set
        {
            GetPropertyByName("Hint").SetValue(_BaseControl, value);
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public string Name
    {
        get { return _BaseControl.Name; }
        set
        {
            GetPropertyByName("Name").SetValue(_BaseControl, value);
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public Color HintColor
    {
        get { return _BaseControl.HintColor; }
        set
        {
            GetPropertyByName("HintColor").SetValue(_BaseControl, value);
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public Color TextColor
    {
        get { return _BaseControl.TextColor; }
        set
        {
            GetPropertyByName("TextColor").SetValue(_BaseControl, value);
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public HorizontalAlignment HintAlignment
    {
        get { return _BaseControl.HintAlignment; }
        set
        {
            GetPropertyByName("HintAlignment").SetValue(_BaseControl, value);
            this.designerActionUISvc.Refresh(this.Component);
        }
    }

    public override DesignerActionItemCollection GetSortedActionItems()
    {
        DesignerActionItemCollection items = new DesignerActionItemCollection();

        //Define static section header entries.
        items.Add(new DesignerActionHeaderItem("Design"));
        items.Add(new DesignerActionHeaderItem("Behaviour"));
        items.Add(new DesignerActionHeaderItem("Appearance"));

        //Boolean property for locking color selections.

        items.Add(new DesignerActionPropertyItem("IsPassword",
                         "Password Field", "Behaviour",
                         "Is it a password field ?"));
       items.Add(new DesignerActionPropertyItem("MultiLine",
                         "MultiLine", "Behaviour",
                         "Multiline Text Field"));
            items.Add(new DesignerActionPropertyItem("HintColor",
                             "Hint Color", "Appearance",
                             "Sets the Hint Text color."));
            items.Add(new DesignerActionPropertyItem("TextColor",
                             "Text Color", "Appearance",
                             "Sets the Text Color."));
            items.Add(new DesignerActionPropertyItem("HintAlignment",
                             "HintAlignment", "Appearance",
                             "Sets the Hint Alignment."));
            //This next method item is also added to the context menu 
            // (as a designer verb).
            
        items.Add(new DesignerActionPropertyItem("Name",
                         "Control Name", "Design",
                         "Sets the Control Name."));
        items.Add(new DesignerActionPropertyItem("Hint",
                        "Hint", "Design",
                        "Sets the Hint text."));

        items.Add(new DesignerActionMethodItem(this,
                             "DefaultColors", "Set Default Colors",
                             "Appearance",
                             "Sets default Hint and Text colors.",
                              true));

        return items;
    }
    public void DefaultColors()
    {
        this._BaseControl.HintColor = Color.Gray;
        this._BaseControl.TextColor = Color.Black;
        this.designerActionUISvc.Refresh(this.Component);
    }

您现在要做的就是在我们准备好的控件类顶部添加 designer 类型属性。

[Designer(typeof(ColorLabelDesigner))]
public class HintTextBox
结果

Screenshot - HintTextBox_Designer_3.png

对于那些尝试修改智能标签 UI 的人,请不要忘记添加对System.Design.dll 的引用,您可以在此处找到它。

Screenshot - HintTextBox_AddReference.png

使用控件

要使用该控件,请先将 HintTextBox.dll 拖到工具栏(任意工具栏)上,您应该能看到一个名为“HintTextBox”的控件。

这样,您就可以开始使用了……将其拖放到任何窗体上,并按需使用它……

请记住,使用 InternalText 属性来访问用户输入文本,而不是使用传统的 Text 属性。

Screenshot - HintTextBox_Form_Code1.png

历史

  1. 添加了 HintAlignment 属性。
  2. 修正了源代码,使 ForeColor 属性能够正常工作。
  3. 注意:新属性名称:TextColor

  4. 修改了智能标签以添加额外属性以及它们的分类支持。
© . All rights reserved.