none
Winform 自定义控件,重绘TextBox RRS feed

  • 问题

  • 我自定义了一个控件,继承自UserControl;里面组合了一个PirctureBox和TextBox,我重写了UserControl的WndProc方法,其目的是绘制UserControl的边框和像TextBox的水印文字提示效果,像html input 标签里的属性PlaceHolder那样,也就是当这个TextBox框失去焦点或者里面的字符长度为0,并且提示文字长度大于0的时候,就出现提示文字,否则就不出现。但是现在却是这样的情况:
    控件被拖动到窗体之后,水印文字显示出来没问题,但是当鼠标点击窗体的其他区域之后,水印文字就会消失一大部分,并且运行的时候TextBox里的水印提示也只显示了一小部分,失去焦点也不消失,输入了文字也不消失。

    代码:

    public partial class MyPicturerTextBox : UserControl

        {

            public MyPicturerTextBox()

            {

                this.DoubleBuffered = true;

                InitializeComponent();

                this.BorderStyle = BorderStyle.FixedSingle;

            }

            private Image userImg;

            [Description("文本框里的图片")]

            public Image UserImg

            {

                get { return userImg; }

                set

                {

                    if (value != null)

                    {

                        this.Img.Image = value;

                    }

                    userImg = value;

                }

            }

            private string txt;

            [Description("输入的文本")]

            public string Txt

            {

                get { return txt; }

                set

                {

                    if (!string.IsNullOrEmpty(value))

                    {

                        this.textBox.Text = value;

                    }

                    txt = value;

                }

            }

            private string promotedText="请输入...";

            [Description("提醒的文字")]

            public string PromotedText

            {

                get { return promotedText; }

                set { promotedText = value;

                    //this.Invalidate();

                }

            }

            [Description("TextChanged事件")]

            public event EventHandler MyTextChanged;

            private void textBox_TextChanged(object sender, EventArgs e)

            {

                MyTextChanged?.Invoke(sender, e);

            }

          

            /// <summary>

            /// 获得当前进程,以便重绘控件

            /// </summary>

            /// <param name="hWnd"></param>

            /// <returns></returns>

            [System.Runtime.InteropServices.DllImport("user32.dll")]

            static extern IntPtr GetWindowDC(IntPtr hWnd);

            [System.Runtime.InteropServices.DllImport("user32.dll")]

            static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

            protected override void WndProc(ref Message m)

            {

                base.WndProc(ref m);

              

                if (m.Msg == 0xf || m.Msg == 0x133)

                {

                  

                    IntPtr hDC = GetWindowDC(m.HWnd);

                    if (hDC.ToInt32() == 0)

                    {

                        return;

                    }

                    //只有在边框样式为FixedSingle时自定义边框样式才有效

                    if (this.BorderStyle == BorderStyle.FixedSingle)

                    {

                        System.Drawing.Graphics g = Graphics.FromHdc(hDC);

                        //边框Width1个像素

                        System.Drawing.Pen pen = new Pen(Brushes.DarkRed, 1);

                        //绘制边框

                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                        g.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);

                        pen.Dispose();

                    }

                    if (!this.textBox.Focused && (this.textBox.TextLength == 0) && promotedText.Length>0)

                    {

                        Graphics g1 = this.textBox.CreateGraphics();

                        g1.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

                        TextFormatFlags tff = (TextFormatFlags.EndEllipsis |

                        TextFormatFlags.NoPrefix | TextFormatFlags.Left |

                        TextFormatFlags.Top | TextFormatFlags.NoPadding);

                        Rectangle rec = this.textBox.ClientRectangle;

                        rec.Offset(2, 1);

                        TextRenderer.DrawText(g1, promotedText, new Font("微软雅黑",10), rec, SystemColors.GrayText,Color.White,tff);

                        g1.Dispose();

                    }

                    //返回结果

                    m.Result = IntPtr.Zero;

                    //释放

                    ReleaseDC(m.HWnd, hDC);

                }

            }

            protected override void OnResize(EventArgs e)

            {

                base.OnResize(e);

                this.Refresh();

            }

        }

    2019年8月5日 12:25

全部回复

  • Hi,

    我这里找到了一篇文章是通过重写一个TextBox类来实现水印提示,你可以参考一下。

    WaterMark TextBox For Desktop Applications Using C#, .NET 3.5 and VS2008

    测试结果如下:

    Regards,

    Kyle


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年8月6日 5:52
    版主
  • 我主要是即为UserControl绘制边框,也要为UserControl里的TextBox绘制水印,我试过只是绘制TextBox的水印是能行的。

    另外我也很想看看你发的链接,可是我的网络无法访问到这个网页。

    2019年8月6日 11:19
  • Hi,

    重写TextBox类代码如下,

        class WaterMarkTextBox : TextBox
        {
            private Font oldFont = null;
            private Boolean waterMarkTextEnabled = false;
    
            #region Attributes 
                private Color _waterMarkColor = Color.Gray;
                public Color WaterMarkColor
                {
                    get { return _waterMarkColor; }
                    set { _waterMarkColor = value; Invalidate();/*thanks to Bernhard Elbl
                                                                  for Invalidate()*/ }
                }
    
                private string _waterMarkText = "Water Mark";
                public string WaterMarkText
                {
                    get { return _waterMarkText; }
                    set { _waterMarkText = value; Invalidate(); }
                }
            #endregion
    
            //Default constructor
            public WaterMarkTextBox()
            {
                JoinEvents(true);
            }
    
            //Override OnCreateControl ... thanks to  "lpgray .. codeproject guy"
            protected override void OnCreateControl() 
            { 
                base.OnCreateControl();
                WaterMark_Toggel(null, null); 
            }
            
            //Override OnPaint
            protected override void OnPaint(PaintEventArgs args)
            {
                // Use the same font that was defined in base class
                System.Drawing.Font drawFont = new System.Drawing.Font(Font.FontFamily,
                    Font.Size, Font.Style, Font.Unit);
                //Create new brush with gray color or 
                SolidBrush drawBrush = new SolidBrush(WaterMarkColor);//use Water mark color
                //Draw Text or WaterMark
                args.Graphics.DrawString((waterMarkTextEnabled ? WaterMarkText : Text),
                    drawFont, drawBrush, new PointF(0.0F, 0.0F));
                base.OnPaint(args);
            }
    
            private void JoinEvents(Boolean join)
            {
                if (join)
                {
                    this.TextChanged += new System.EventHandler(this.WaterMark_Toggel);
                    this.LostFocus += new System.EventHandler(this.WaterMark_Toggel);
                    this.FontChanged += new System.EventHandler(this.WaterMark_FontChanged);
                    //No one of the above events will start immeddiatlly 
                    //TextBox control still in constructing, so,
                    //Font object (for example) couldn't be catched from within
                    //WaterMark_Toggle
                    //So, call WaterMark_Toggel through OnCreateControl after TextBox
                    //is totally created
                    //No doupt, it will be only one time call
                    
                    //Old solution uses Timer.Tick event to check Create property
                }
            }
    
            private void WaterMark_Toggel(object sender, EventArgs args )
            {
                if (this.Text.Length <= 0)
                    EnableWaterMark();
                else
                    DisbaleWaterMark();
            }
    
            private void EnableWaterMark()
            {
                //Save current font until returning the UserPaint style to false (NOTE:
                //It is a try and error advice)
                oldFont = new System.Drawing.Font(Font.FontFamily, Font.Size, Font.Style,
                   Font.Unit);
                //Enable OnPaint event handler
                this.SetStyle(ControlStyles.UserPaint, true);
                this.waterMarkTextEnabled = true;
                //Triger OnPaint immediatly
                Refresh();
            }
    
            private void DisbaleWaterMark()
            {
                //Disbale OnPaint event handler
                this.waterMarkTextEnabled = false;
                this.SetStyle(ControlStyles.UserPaint, false);
                //Return back oldFont if existed
                if(oldFont != null)
                    this.Font = new System.Drawing.Font(oldFont.FontFamily, oldFont.Size,
                        oldFont.Style, oldFont.Unit);
            }
    
            private void WaterMark_FontChanged(object sender, EventArgs args)
            {
                if (waterMarkTextEnabled)
                {
                    oldFont = new System.Drawing.Font(Font.FontFamily,Font.Size,Font.Style,
                        Font.Unit);
                    Refresh();
                }
            }
        }

    你可以参考一下。

    Regards,

    Kyle


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年8月7日 9:06
    版主