Я пытаюсь нарисовать прямоугольник вокруг окна (внутри окна, а не снаружи), но рисунок мажет, а в некоторых случаях не перерисовывается.
Я подклассифицировал 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, получили те же результаты.
Благодаря ответам 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 в начале, потому что я должен сначала нарисовать приложение, а затем мне нужно сделать так, чтобы я был наверху.
Если рисование границы - это все, что необходимо, то можем ли мы иметь более простой механизм, как показано ниже?
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);
}
}
Я не уверен, так как не могу проверить его и не знаю фона переменной-члена g
но я думаю, что это проблема с отсекающим прямоугольником. Поэтому вы можете попробовать что-то подобное в своей функции WndProc
чтобы убедиться, что все окно перерисовано:
case 0x05: // WM_SIZE
InvalidateRect(this.Handle, GetWndRect(this.Handle), TRUE);
break;
Во-первых, вы не можете вызвать 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);
}
}
GetWndRect
?