Рисование внутри окна не перерисовывается и не смазывается

1

Я пытаюсь нарисовать прямоугольник вокруг окна (внутри окна, а не снаружи), но рисунок мажет, а в некоторых случаях не перерисовывается.

Я подклассифицировал 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, получили те же результаты.

  • 0
    Что именно происходит в GetWndRect ?
  • 0
    @Fratyx - добавил его в :-) спасибо!
Показать ещё 1 комментарий
Теги:
winapi
gdi+

4 ответа

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 в начале, потому что я должен сначала нарисовать приложение, а затем мне нужно сделать так, чтобы я был наверху.

  • 1
    Не забудьте Dispose Graphics позже, желательно с using оператора using .
1

Если рисование границы - это все, что необходимо, то можем ли мы иметь более простой механизм, как показано ниже?

  1. Установите правку перерисовки на изменение размера.
  2. Переопределить OnPaint

Пример. Предполагая, что выбранное вами окно является 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); 
        }
    }
  • 0
    Спасибо за идею, но я подклассифицирую собственное окно (любое заданное окно)
  • 0
    Извинения, я полностью пропустил это!
1

Я не уверен, так как не могу проверить его и не знаю фона переменной-члена g но я думаю, что это проблема с отсекающим прямоугольником. Поэтому вы можете попробовать что-то подобное в своей функции WndProc чтобы убедиться, что все окно перерисовано:

case 0x05: // WM_SIZE
  InvalidateRect(this.Handle, GetWndRect(this.Handle), TRUE);
  break;
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);
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню