Cod*_*cat 7 c# controls paintevent winforms
我一直在尝试为现有的.Net WinForms控件绘制自定义边框.我试过这个是通过创建一个类来控制我想要改变它的边框颜色,然后在绘画过程中尝试几个东西.我尝试过以下方法:
1.抓住WM_NCPAINT.这有点起作用.下面的代码的问题是,当控件调整大小时,边框将在右侧和底侧被切断.不好.
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_NCPAINT) {
WmNcPaint(ref m);
return;
}
base.WndProc(ref m);
}
private void WmNcPaint(ref Message m)
{
if (BorderStyle == BorderStyle.None) {
return;
}
IntPtr hDC = NativeMethods.GetWindowDC(m.HWnd);
if (hDC != IntPtr.Zero) {
using (Graphics g = Graphics.FromHdc(hDC)) {
ControlPaint.DrawBorder(g, new Rectangle(0, 0, this.Width, this.Height), _BorderColor, ButtonBorderStyle.Solid);
}
m.Result = (IntPtr)1;
NativeMethods.ReleaseDC(m.HWnd, hDC);
}
}
Run Code Online (Sandbox Code Playgroud)
2.覆盖void OnPaint.这适用于某些控件,但不是全部.这也需要你设置BorderStyle为BorderStyle.None,你必须手动清除油漆背景,否则你在调整大小时得到这个.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder(e.Graphics, new Rectangle(0, 0, this.Width, this.Height), _BorderColor, ButtonBorderStyle.Solid);
}
Run Code Online (Sandbox Code Playgroud)
3.覆盖void OnResize和void OnPaint(如方法2).通过这种方式,它可以很好地调整大小,但是当Panel AutoScroll启用时则不会,在这种情况下,向下滚动时它将看起来像这样.如果我尝试使用WM_NCPAINT边框绘制,Refresh()则无效.
protected override void OnResize(EventArgs eventargs)
{
base.OnResize(eventargs);
Refresh();
}
Run Code Online (Sandbox Code Playgroud)
建议非常欢迎.我想知道最好的方法是什么,对于多种类型的控件(我必须为多个默认的WinForms控件执行此操作).
编辑:所以我弄清楚是什么导致了我最初的问题。经过很长时间的修补、试验和研究 .Net 框架源代码,这里有一个明确的方法来实现它(考虑到您有一个继承自要在其上绘制自定义边框的控件的控件):
[DllImport("user32.dll")]
public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);
[Flags()]
public enum RedrawWindowFlags : uint
{
Invalidate = 0X1,
InternalPaint = 0X2,
Erase = 0X4,
Validate = 0X8,
NoInternalPaint = 0X10,
NoErase = 0X20,
NoChildren = 0X40,
AllChildren = 0X80,
UpdateNow = 0X100,
EraseNow = 0X200,
Frame = 0X400,
NoFrame = 0X800
}
// Make sure that WS_BORDER is a style, otherwise borders aren't painted at all
protected override CreateParams CreateParams
{
get
{
if (DesignMode) {
return base.CreateParams;
}
CreateParams cp = base.CreateParams;
cp.ExStyle &= (~0x00000200); // WS_EX_CLIENTEDGE
cp.Style |= 0x00800000; // WS_BORDER
return cp;
}
}
// During OnResize, call RedrawWindow with Frame|UpdateNow|Invalidate so that the frame is always redrawn accordingly
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (DesignMode) {
RecreateHandle();
}
RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Frame | RedrawWindowFlags.UpdateNow | RedrawWindowFlags.Invalidate);
}
// Catch WM_NCPAINT for painting
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_NCPAINT) {
WmNcPaint(ref m);
return;
}
base.WndProc(ref m);
}
// Paint the custom frame here
private void WmNcPaint(ref Message m)
{
if (BorderStyle == BorderStyle.None) {
return;
}
IntPtr hDC = NativeMethods.GetWindowDC(m.HWnd);
using (Graphics g = Graphics.FromHdc(hDC)) {
g.DrawRectangle(new Pen(_BorderColor), new Rectangle(0, 0, this.Width - 1, this.Height - 1));
}
NativeMethods.ReleaseDC(m.HWnd, hDC);
}
Run Code Online (Sandbox Code Playgroud)
简而言之,保持 OnPaint 不变,确保WS_BORDER已设置,然后WM_NCPAINT通过 hDC 捕获并绘制边框,并确保RedrawWindow在OnResize.
这甚至可以扩展以绘制自定义滚动条,因为这是窗口框架的一部分,您可以在WM_NCPAINT.
我从中删除了我的旧答案。
编辑2:对于ComboBox,您必须捕获WM_PAINT,WndProc()因为由于某种原因,用于绘制 的 .Net 源ComboBox不使用OnPaint(),但是WM_PAINT。所以像这样:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == NativeMethods.WM_PAINT) {
OnWmPaint();
}
}
private void OnWmPaint()
{
using (Graphics g = CreateGraphics()) {
if (!_HasBorders) {
g.DrawRectangle(new Pen(BackColor), new Rectangle(0, 0, this.Width - 1, this.Height - 1));
return;
}
if (!Enabled) {
g.DrawRectangle(new Pen(_BorderColorDisabled), new Rectangle(0, 0, this.Width - 1, this.Height - 1));
return;
}
if (ContainsFocus) {
g.DrawRectangle(new Pen(_BorderColorActive), new Rectangle(0, 0, this.Width - 1, this.Height - 1));
return;
}
g.DrawRectangle(new Pen(_BorderColor), new Rectangle(0, 0, this.Width - 1, this.Height - 1));
}
}
Run Code Online (Sandbox Code Playgroud)