我正在创建一个自定义控件(带水印的文本框),它继承自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;
}
}
}
问题是,当文本框获得焦点时,它不会删除水印。
我在这里错过了什么/做错了什么?
答案 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;
}
}
}