Textbox的Overrided Text属性无法正确刷新

时间:2016-05-09 14:18:17

标签: c# winforms textbox override

我正在创建一个自定义控件(带水印的文本框),它继承自Textbox。截至目前,文本框在没有文本时正确地显示失去焦点的水印,并在文本框获得焦点时删除它(如果它是水印,它甚至会更改文本的颜色)。 我想要它做的是报告它在显示水印时没有文字,所以我试图覆盖Text属性。

代码如下:

public class WatermarkedTextbox : TextBox
{
    private bool _isWatermarked;
    private string _watermark;
    public string Watermark
    {
        get { return _watermark; }
        set { _watermark = value; }
    }


    [Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override string Text
    {
        get
        {
            return _isWatermarked ? string.Empty : base.Text;
        }
        set
        {
            base.Text = value;
        }
    }

    public WatermarkedTextbox()
    {
        GotFocus += WatermarkedTextbox_GotFocus;
        LostFocus += WatermarkedTextbox_LostFocus;
    }

    private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
    {
        if (Text.Length == 0)
        {
            ForeColor = SystemColors.InactiveCaption;
            Text = _watermark;
            _isWatermarked = true;
        }
    }

    private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
    {
        if (_isWatermarked)
        {
            ForeColor = SystemColors.ControlText;
            Text = string.Empty;
            _isWatermarked = false;
        }
    }
}

问题是,当文本框获得焦点时,它不会删除水印。

我在这里错过了什么/做错了什么?

4 个答案:

答案 0 :(得分:1)

对不起,我没有清楚地读到这一点。或者,您可能希望不通过覆盖Text属性来通知。

您可以使用事件:

public class WatermarkedTextbox : TextBox, INotifyPropertyChanged
{
    private bool _isWatermarked;
    private string _watermark;
    public string Watermark
    {
        get { return _watermark; }
        set { _watermark = value; }
    }

    public bool IsWaterMarked
    {
        get
        {
            return _isWatermarked;
        }
        set
        {
            _isWatermarked = value;
            OnPropertyChanged("IsWaterMarked");
        }
    }

    public WatermarkedTextbox()
    {
        GotFocus += WatermarkedTextbox_GotFocus;
        LostFocus += WatermarkedTextbox_LostFocus;
    }

    private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
    {
        if (Text.Length == 0)
        {
            ForeColor = SystemColors.InactiveCaption;
            Text = _watermark;
            IsWaterMarked = true;
        }
    }

    private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
    {
        if (_isWatermarked)
        {
            ForeColor = SystemColors.ControlText;
            Text = string.Empty;
            IsWaterMarked = false;
        }
    }

    protected void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, e);
    }

    protected void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

然后在主窗体上,您可以订阅并向PropertyChanged事件添加处理程序:

//somewhere, like in the forms constructor, you need to subscribe to this event
watermarkedTextbox2.PropertyChanged += watermarkedTextbox2_PropertyChanged;

// the handler function
void watermarkedTextbox2_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "IsWaterMarked")
    {
        if (watermarkedTextbox2.IsWaterMarked)
            ; //handle here
        else
            ; //handle here
    }
}

答案 1 :(得分:1)

Windows支持文本框(以及其他编辑控件,如组合框)的水印,他们称之为“提示横幅”。但请注意,这不适用于多行文本框。

在支持的控件上设置cue横幅只是使用Win32 API向包含水印文本的控件发送EM_SETCUEBANNER消息。然后Windows将处理检测控件何时为空或具有焦点并为您完成所有艰苦工作,您将不需要使用事件来管理状态。当您获得控件的Text属性时,也会忽略提示横幅。

我使用以下助手类来设置提示横幅(也适用于组合框):

public class CueBannerHelper
{
    #region Win32 API's
    [StructLayout(LayoutKind.Sequential)]
    public struct COMBOBOXINFO
    {
        public int cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public IntPtr stateButton;
        public IntPtr hwndCombo;
        public IntPtr hwndItem;
        public IntPtr hwndList;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }


    /// <summary>Used to get the current Cue Banner on an edit control.</summary>
    public const int EM_GETCUEBANNER = 0x1502;
    /// <summary>Used to set a Cue Banner on an edit control.</summary>
    public const int EM_SETCUEBANNER = 0x1501;


    [DllImport("user32.dll")]
    public static extern bool GetComboBoxInfo(IntPtr hwnd, ref COMBOBOXINFO pcbi);

    [DllImport("user32.dll")]
    public static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
    #endregion

    #region Method members
    public static void SetCueBanner(Control control, string cueBanner) {
        if (control is ComboBox) {
            CueBannerHelper.COMBOBOXINFO info = new CueBannerHelper.COMBOBOXINFO();
            info.cbSize = Marshal.SizeOf(info);

            CueBannerHelper.GetComboBoxInfo(control.Handle, ref info);

            CueBannerHelper.SendMessage(info.hwndItem, CueBannerHelper.EM_SETCUEBANNER, 0, cueBanner);
        }
        else {
            CueBannerHelper.SendMessage(control.Handle, CueBannerHelper.EM_SETCUEBANNER, 0, cueBanner);
        }
    }
    #endregion
}

在自定义TextBox控件上实现水印所需的全部内容是以下属性(顶部的属性用于控件的设计时属性):

/// <summary>
/// Gets or sets the watermark that the control contains.
/// </summary>
[Description("The watermark that the control contains."),
    Category("Appearance"),
    DefaultValue(null),
    Browsable(true)
]
public string Watermark {
    get { return this._watermark; }
    set {
        this._watermark = value;
        CueBannerHelper.SetCueBanner(this, value);
    }
}

答案 2 :(得分:0)

删除你的覆盖Text属性,然后它就可以了!

删除以下行:

[Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
    get
    {
        return _isWatermarked ? string.Empty : base.Text;
    }
    set
    {
        base.Text = value;
    }
}

答案 3 :(得分:0)

Hans Passant的评论是我问题的正确答案。此外,感谢每个人花时间提供帮助。

我最终决定采用最简单的路线(处理PropertyChanged对于这种特殊需求似乎过于复杂,并且挂钩Windows API会遗漏多行文本框,所以它不是一个选项。)

如果有人需要,请输入以下代码:

public class WatermarkedTextbox : TextBox
{
    private bool _isWatermarked;
    private string _watermark;
    public string Watermark
    {
        get { return _watermark; }
        set { _watermark = value; }
    }

    [Bindable(false), EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override string Text
    {
        get
        {
            return _isWatermarked ? string.Empty : base.Text;
        }
        set
        {
            base.Text = value;
        }
    }

    public WatermarkedTextbox()
    {
        GotFocus += WatermarkedTextbox_GotFocus;
        LostFocus += WatermarkedTextbox_LostFocus;
    }

    private void WatermarkedTextbox_LostFocus(object sender, EventArgs e)
    {
        if (Text.Length == 0)
        {
            _isWatermarked = true;
            ForeColor = SystemColors.InactiveCaption;
            Text = _watermark;
        }
    }

    private void WatermarkedTextbox_GotFocus(object sender, EventArgs e)
    {
        if (_isWatermarked)
        {
            _isWatermarked = false;
            ForeColor = SystemColors.ControlText;
            Text = string.Empty;
        }
    }
}