【问题标题】:Textbox custom onPaint文本框自定义onPaint
【发布时间】:2013-06-29 04:15:42
【问题描述】:

我正在尝试为我的文本框创建自定义 onPaint,它正在工作...it is working 但是当我尝试输入内容时,a textbox is rendering above the textbox。

这是我的构造函数:

public TextBox()
{
  Font = new Font("Segoe UI", 11F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
  BackColor = Color.White;
  BorderColor = Color.Gray;
  BorderStyle = BorderStyle.None;
  SetStyle(ControlStyles.UserPaint, true);
}

还有onPaint:

protected override void OnPaint(PaintEventArgs e)
{
  Graphics g = e.Graphics;
  g.FillRectangle(backgroundBrush, 0, 0, this.Width, this.Height);

  SizeF fontSize = g.MeasureString(Text, Font);
  g.DrawString(Text, Font, new SolidBrush(ForeColor), new PointF(5, 5), cFormat);

  g.DrawRectangle(borderPen, borderPen.Width / 2, borderPen.Width / 2,
                  this.Width - borderPen.Width, this.Height - borderPen.Width);
}

【问题讨论】:

  • TextBox 控件不使用 Paint 事件,因此您看到的是控件的版本和您自己的版本。
  • 对,所以我需要自己控制吗?还是有别的办法?
  • 如果您只想制作边框,请尝试将 TextBox 放在具有 2 像素填充的面板内,并将 TextBox 设置为 Dock.Fill 和 MultiLine=true。否则,不清楚为什么您要尝试绘制 TextBox。
  • @LarsTech 我们想要一个带有自定义边框颜色的文本框。
  • @HighCore 你又来了,我想知道你为什么喜欢参与Winforms 问题,而你认为​​它已经死了。劝人让winforms早点死?我不认为你必须这样做。许多人肯定会认识到WPF 对他们来说更好。我还向您打赌,WPF 将成为我下一个商业项目的选择。但是我仍然会学习/学习Winforms 以练习其他技能,例如 GDI+、Win32 等……只是为了好玩。

标签: c# winforms


【解决方案1】:

如果您只想要一个带有一些自定义边框(宽度和颜色)的自定义 TextBox,我这里有 2 个解决方案:

  1. 使用ControlPaint,这将允许您使用一些样式和颜色绘制边框,但不能使用Brush 进行更多样化的绘制(就像HatchBrush 一样):

    public class CustomTextBox : TextBox
    {
        [DllImport("user32")]
        private static extern IntPtr GetWindowDC(IntPtr hwnd);
        struct RECT
        {
          public int left, top, right, bottom;
        }
        struct NCCALSIZE_PARAMS
        {
          public RECT newWindow;
          public RECT oldWindow;
          public RECT clientWindow;
          IntPtr windowPos;
        }            
        float clientPadding = 2;  
        float actualBorderWidth = 4;
        Color borderColor = Color.Red;      
        protected override void WndProc(ref Message m)
        {
          //We have to change the clientsize to make room for borders
          //if not, the border is limited in how thick it is.
          if (m.Msg == 0x83) //WM_NCCALCSIZE   
          {
            if (m.WParam == IntPtr.Zero)
            {
                RECT rect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
                rect.left += clientPadding;
                rect.right -= clientPadding;
                rect.top += clientPadding;
                rect.bottom -= clientPadding;
                Marshal.StructureToPtr(rect, m.LParam, false);
            }
            else
            {
                NCCALSIZE_PARAMS rects = (NCCALSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALSIZE_PARAMS));
                rects.newWindow.left += clientPadding;
                rects.newWindow.right -= clientPadding;
                rects.newWindow.top += clientPadding;
                rects.newWindow.bottom -= clientPadding;
                Marshal.StructureToPtr(rects, m.LParam, false);
            }
          }
          if (m.Msg == 0x85) //WM_NCPAINT    
          {         
             IntPtr wDC = GetWindowDC(Handle);
             using(Graphics g = Graphics.FromHdc(wDC)){                                                      
               ControlPaint.DrawBorder(g, new Rectangle(0,0,Size.Width, Size.Height), borderColor, actualBorderWidth, ButtonBorderStyle.Solid,
             borderColor, actualBorderWidth, ButtonBorderStyle.Solid, borderColor, actualBorderWidth, ButtonBorderStyle.Solid,
             borderColor, actualBorderWidth, ButtonBorderStyle.Solid); 
             }   
             return;          
          }
          base.WndProc(ref m);
        }
    }
    

    这是文本框快照:

  2. 使用Graphics的FillRegion方法用各种Brush绘制边框,这里我使用HatchBrush:

    public class CustomTextBox : TextBox
    {
      [DllImport("user32")]
      private static extern IntPtr GetWindowDC(IntPtr hwnd);
      struct RECT
      {
        public int left, top, right, bottom;
      }
      struct NCCALSIZE_PARAMS
      {
        public RECT newWindow;
        public RECT oldWindow;
        public RECT clientWindow;
        IntPtr windowPos;
      }         
      int clientPadding = 2;   
      int actualBorderWidth = 4;     
      protected override void WndProc(ref Message m)
      {
          //We have to change the clientsize to make room for borders
          //if not, the border is limited in how thick it is.
          if (m.Msg == 0x83) //WM_NCCALCSIZE   
          {
            if (m.WParam == IntPtr.Zero)
            {
                RECT rect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
                rect.left += clientPadding;
                rect.right -= clientPadding;
                rect.top += clientPadding;
                rect.bottom -= clientPadding;
                Marshal.StructureToPtr(rect, m.LParam, false);
            }
            else
            {
                NCCALSIZE_PARAMS rects = (NCCALSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALSIZE_PARAMS));
                rects.newWindow.left += clientPadding;
                rects.newWindow.right -= clientPadding;
                rects.newWindow.top += clientPadding;
                rects.newWindow.bottom -= clientPadding;
                Marshal.StructureToPtr(rects, m.LParam, false);
            }
          }
          if (m.Msg == 0x85) //WM_NCPAINT
          {                 
            IntPtr wDC = GetWindowDC(Handle);
            using(Graphics g = Graphics.FromHdc(wDC)){                                                
              Rectangle rect = new Rectangle(0,0,Width,Height);
              Rectangle inner = new Rectangle(0, 0, Width, Height);
              inner.Offset(actualBorderWidth + 2, actualBorderWidth + 2);
              inner.Width -= 2 * actualBorderWidth + 4;
              inner.Height -= 2 * actualBorderWidth + 4;
              Region r = new Region(rect);
              r.Xor(inner);
              using (System.Drawing.Drawing2D.HatchBrush brush = new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.SmallCheckerBoard, Color.Green, Color.Red))
              {                    
                g.FillRegion(brush, r);
              }
            }
            return;
          }
          base.WndProc(ref m);
      }
    }
    

    这是文本框快照:

【讨论】:

  • 我尝试了代码,但它不起作用:System.ArgumentException 未处理,参数无效。在第 62 行 (g.ReleaseHdc(wDC))。它还说它不能将float转换为int,所以我将actualBorderWidth更改为int。
  • @gerard 哪个代码不起作用?第一个还是第二个? actualBorderWidth 应该在第二个代码中声明为 int,我更新了它,对不起,但在第一个代码中它应该是 float。
  • @gerard 请查看我的更新,只需删除g.ReleaseHdc... 行并添加return; 行。我不知道为什么它以前有效,但在这种情况下,我们不需要 ReleaseDC,如果你愿意,你可以使用ReleaseDC win32 api 函数,它不会抛出任何异常。需要return;,以便不执行默认的非客户区绘制。
  • @gerard 此代码严重泄漏。您需要使用ReleaseDC,而不是.G.ReleaseHdc,并在处理图形对象后调用它ReleaseDC(Handle, wDC);,该对象应包装在using 分支using (Graphics g = Graphics.FromHdc(wDC)) {...} 中。我认为(我可能是错的)但 return 语句可能存在问题,阻止调用 base.WndProc(ref m); 方法:我不认为你总是在 Vista 或 Win7 上的多行框中绘制滚动条。
  • 不要忽视 ReleaseDC 上的建议。您正在使用当前代码泄漏句柄。
猜你喜欢
  • 1970-01-01
  • 2013-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多