在窗口内绘图不会重绘和涂抹

时间:2014-10-15 05:46:05

标签: c# windows winapi gdi+

我试图在窗口周围绘制一个矩形(窗口内部,而不是外部),但绘图涂抹,在某些情况下不会重绘。

我将HWND WndProc子类化(当然,代码在HWND进程中运行):

class SubClasser : NativeWindow
{
 ...
    protected override void WndProc(ref Message m)
    {
        switch(m.Msg)
        {
            case 0x85: // WM_CPAINT
            case 0xf:  // WM_PAINT
            {
                base.WndProc(ref m);
                Rectangle r = GetWndRect(this.Handle);
                g.DrawRectangle(p, r);
                Trace.WriteLine("WM_PAINT: "+r.ToString());
            }
            break;

            default:
                Trace.WriteLine("0x" + m.Msg.ToString("X"));
                base.WndProc(ref m);
            break;
        }
    }
 ...
}

private Rectangle RECTtoRectangle(RECT r)
{
    return new Rectangle(r.Left, r.Top, r.Right, r.Bottom);
}
private Rectangle GetWndRect(IntPtr hwnd)
{
    RECT r = new RECT();
    GetClientRect(hwnd, out r);

    return RECTtoRectangle(r);
}

正如您在代码中看到的那样,我正在重新绘制"矩形"在WM_PAINT和WM_CPAINT上,但还不够:

  • 当我将窗口放大(调整大小)时,矩形不会变大。
  • 当我缩小窗口(再次调整大小)时,矩形会变小,但是当我放大窗口时,矩形会被涂抹到原始大小(不大)。
  • 当我将窗口移到可见屏幕之外时,矩形的一部分(仅底部)不会被重绘。

我应该指出,我确实得到了绘制消息,似乎子类化工作正常。

我真的被困: - (

编辑:

OH,我也试过放置:

base.WndProc(ref m);

仅在WndProc的末尾,得到了相同的结果。

4 个答案:

答案 0 :(得分:1)

我不确定因为我无法测试它并且不知道成员变量g的背景但是我认为这是剪切矩形的问题。因此,您可以在WndProc函数中尝试这样的操作,以确保重绘整个窗口:

case 0x05: // WM_SIZE
  InvalidateRect(this.Handle, GetWndRect(this.Handle), TRUE);
  break;

答案 1 :(得分:1)

首先,您无法在消息上多次致电base.WndProc(ref m);;

其次,每次更改窗口大小时都应重新创建g,因此它可以在更大的表面上绘制。

第三,在窗口上调用Invalidate以在窗口大小改变时强制重绘。我使用SizeChanged事件和ClientSize,因为我可以轻松编译代码(需要阅读文档来编写GDI + / WINAPI)。

public partial class RedrawInWndProcForm : Form
{
    public RedrawInWndProcForm()
    {
        InitializeComponent();
        p = new Pen(Color.Red, 2.0f);
        this.SizeChanged += (s, e) => { this.Invalidate(); };
    }


    Graphics g; 
    Pen p;
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0xf:  // WM_PAINT
                {
                    g = Graphics.FromHwnd(this.Handle);
                    Rectangle r = GetWndRect(this.Handle);
                    g.DrawRectangle(p, r);
                    Trace.WriteLine("WM_PAINT: " + r.ToString());
                }
                break;
        }
        Trace.WriteLine("handled");
        base.WndProc(ref m);
    }

    private Rectangle GetWndRect(IntPtr hwnd)
    {
        return new Rectangle(0, 0, (int)this.ClientSize.Width, (int)this.ClientSize.Height);
    }
}

答案 2 :(得分:1)

如果只需要绘制边框,那么我们可以使用更简单的机制吗?

  1. 将重新调整大小设置为true。
  2. 覆盖OnPaint
  3. 实施例: 假设您正在进行子类化的窗口是Form

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                SetStyle(ControlStyles.ResizeRedraw, true); // this is important
            }
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                Rectangle rcBorder = e.ClipRectangle;
                rcBorder.Inflate(-10, -10); // just to accentuate with red colored border
                e.Graphics.DrawRectangle(Pens.Red, rcBorder); 
            }
        }
    

答案 3 :(得分:1)

感谢kennyzx和Fratyx的回答,我得到了一个有效的解决方案。

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        switch(m.Msg)
        {
            case 0x85: // WM_CPAINT
            case 0xf:  // WM_PAINT
            {
                g = Graphics.FromHwnd(this.Handle);
                Rectangle r = GetWndRect(this.Handle);
                g.DrawRectangle(p, r);
                Trace.WriteLine("WM_PAINT: "+r.ToString());
            }
            break;

            case 0x05: // WM_SIZE
            {
                InvalidateRect(this.Handle, IntPtr.Zero, true);
                Trace.WriteLine("WM_SIZE");
            }
            break;

            default:
                Trace.WriteLine("0x" + m.Msg.ToString("X"));
            break;
        }
    }

注意base.WndProc在开头,因为我必须首先让应用程序绘制,然后我需要绘制,所以我将在顶部。