【问题标题】:Overrided Text property of a Textbox does not refresh properly文本框的覆盖文本属性未正确刷新
【发布时间】:2016-05-10 21:56:20
【问题描述】:

我正在创建一个自定义控件(带水印的文本框),它继承自 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;
        }
    }
}

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

我在这里遗漏了什么/做错了什么?

【问题讨论】:

  • 你可以创建一个新属性并在 Text override 中设置它的值吗?
  • Winforms 也会调用您覆盖的 Text 属性 getter。用于检查 TextBox 是否需要重新绘制。当它出现时,isWatermarked 字段还没有正确的值。只需将 isWatermarked 赋值移到 文本赋值之前,它就会起作用。
  • Hans Passant,请将此作为答案而不是评论发布,以便我将其标记为解决方案。谢谢你的解释。

标签: c# winforms textbox overriding


【解决方案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
    }
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      删除您覆盖的 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;
          }
      }
      

      【讨论】:

      • 重写 Text 属性的目的是报告文本框在显示水印时没有文本。不这样做会告诉每个人检查文本框是否有有效文本,而实际上没有。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2013-03-25
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 2019-02-25
        • 2018-04-25
        • 1970-01-01
        • 1970-01-01
        • 2013-10-03
        相关资源
        最近更新 更多