Как рисовать пользовательские границы on.Net элементы управления 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);
}
}
2. Переопределение void OnPaint
. Это работает для некоторых элементов управления, но не для всех. Это также требует, чтобы вы установили BorderStyle
to 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);
}
3. Переопределение void OnResize
и void OnPaint
(как в методе 2). Таким образом, он хорошо рисует с изменением размера, но не тогда, когда панель имеет AutoScroll
включено, и в этом случае это будет выглядеть так при прокрутке вниз. Если я попытаюсь использовать WM_NCPAINT
в нарисуйте границу,Refresh()
не имеет никакого эффекта.
protected override void OnResize(EventArgs eventargs)
{
base.OnResize(eventargs);
Refresh();
}
предложения, более чем приветствуется. Я хотел бы знать, что лучшие способ сделать это, для нескольких типов элементов управления (мне придется сделать это для нескольких элементов управления WinForms по умолчанию).
2 ответов
EDIT: поэтому я понял, что вызвало мои первоначальные проблемы. После очень долгого времени возиться, экспериментировать и глядя в исходный код .Net framework, вот окончательный способ сделать это (учитывая, что у вас есть элемент управления, который наследуется от элемента управления, который вы хотите нарисовать пользовательскую границу):
[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);
}
Итак, в двух словах, оставьте OnPaint как есть, убедитесь WS_BORDER
установлен, затем catch WM_NCPAINT
и нарисуйте границу через hDC и убедитесь, что RedrawWindow
называется OnResize
.
это может быть даже расширено, чтобы нарисовать пользовательскую полосу прокрутки, потому что это часть оконной рамы, которую вы можете нарисовать во время WM_NCPAINT
.
Я удалил свой старый ответ от этого.
EDIT 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));
}
}
на самом деле вы можете использовать элементы управления совместимости WPF для создания любой границы.
- Создать Форму
- поместите элемент управления ElementHost (из взаимодействия WPF) в форму
- создайте пользовательский элемент управления WPF (или используйте существующую панель) с пользовательской границей
- поместите элемент управления WindowsFormsHost в пользовательский элемент управления WPF (этот элемент управления будет использоваться позже для размещения элемента управления )
-
задайте дочернее свойство ElementHost с помощью WPF Пользовательский контроль с предыдущего шага
Я согласен, что мое решение содержит много вложенных элементов управления, но, с моей точки зрения, это значительно уменьшает количество проблем, связанных с OnPaint