文本框的覆盖文本属性不能正确刷新

问题描述:

我正在创建自定义控件(水印文本框),并且它从文本框继承。截至目前,文本框在没有文本时正确显示失去焦点时的水印,并在文本框获得焦点时删除它(即使文本的颜色是水印也会更改)。 我想要它做的是报告它没有文字时显示水印,所以我试图重写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; 
     } 
    } 
} 

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

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

+0

可你只是做一个新的属性,然后在文本覆盖设置它的价值呢? – Jacobr365

+0

可能的重复[如何实现显示“Type here”的TextBox?](http://*.com/questions/2487104/how-do-i-implement-a-textbox-that-displays-type-这里) – raidensan

+1

您的重写的Text属性获取器也被Winforms调用。用于检查文本框是否需要重新绘制。如果是这样,isWatermarked字段还没有正确的值。在文本分配前简单地移动isWatermarked赋值*,它将起作用。 –

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

我终于决定走最简单的路线(处理PropertyChanged似乎太复杂,因为这个特殊的需要和挂钩Windows APIs留下多行文本框,所以它不是一个选项)。

万一有人需要它,下面的代码:

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; 
     } 
    } 
} 

删除你的覆盖文本属性,那么它将工作!

删除这些行:

[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; 
    } 
} 
+0

重写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 
    } 
} 

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

在受支持的控件上设置提示横幅仅仅是使用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); 
    } 
}