Пользовательский дескриптор изменения размера в форме C без границ#
Я пытаюсь сделать формы без границ, которые выскакивают из панели инструментов. Я хочу, чтобы пользователь мог захватить в правом нижнем углу ("ручка изменения размера") и иметь возможность изменять размер формы, но не иметь возможности изменять размер или перемещать форму каким-либо другим способом.
Я слышал, что могу перехватить WM_NCHITTEST
сообщение отправляется в форму и устанавливает его результат в HTBOTTOMRIGHT
что позволит операционной системе обрабатывать изменение размера формы, как если бы у нее был значительный кадр. Идея I должен был определить, вошел ли указатель мыши в поле, которое я определил в углу, и если он это сделал, то вернул HTBOTTOMRIGHT
результат.
Это не совсем работает, как я ожидал. Я могу перехватить сообщение, но кажется, что сообщение отправляется только тогда, когда пользователь помещает курсор мыши на толстую рамку в 1px формы. Это означает, что он работает так, как я хочу, если вы очень точно поместите курсор на нижние правые края.
здесь мой WndProc
переопределение:
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 HTBOTTOMRIGHT = 17;
const int RESIZE_HANDLE_SIZE = 40;
bool handled = false;
if (m.Msg == WM_NCHITTEST)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Rectangle hitBox = new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
if (hitBox.Contains(clientPoint))
{
m.Result = (IntPtr)HTBOTTOMRIGHT;
handled = true;
}
}
if (!handled)
base.WndProc(ref m);
}
я делаю что-то неправильно или есть лучший способ сделать то, что я пытаюсь сделать?
спасибо.
4 ответов
Я искал что-то подобное и код Антона был отличный выбор. Это то, что я закончил, чтобы изменить размер работы со всех сторон. Я не уверен a Dictionary
был оптимальным способом хранения hitboxes, но я думаю, что это не имеет большого значения.
и поскольку моя форма заполнена элементами управления, использующими Fill в качестве параметров док-станции, мне просто нужно было добавить 5px-заполнение в Form
для того, чтобы хорошо работать.
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 WM_MOUSEMOVE = 0x0200;
const UInt32 HTLEFT = 10;
const UInt32 HTRIGHT = 11;
const UInt32 HTBOTTOMRIGHT = 17;
const UInt32 HTBOTTOM = 15;
const UInt32 HTBOTTOMLEFT = 16;
const UInt32 HTTOP = 12;
const UInt32 HTTOPLEFT = 13;
const UInt32 HTTOPRIGHT = 14;
const int RESIZE_HANDLE_SIZE = 10;
bool handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
{HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
{HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
};
foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
{
if (hitBox.Value.Contains(clientPoint))
{
m.Result = (IntPtr) hitBox.Key;
handled = true;
break;
}
}
}
if (!handled)
base.WndProc(ref m);
}
просто небольшая модификация вашего кода. Я добавил WM_MOUSEMOVE
обработка сообщений:
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 WM_MOUSEMOVE = 0x0200;
const UInt32 HTBOTTOMRIGHT = 17;
const int RESIZE_HANDLE_SIZE = 10;
bool handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE )
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Rectangle hitBox = new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
if (hitBox.Contains(clientPoint))
{
m.Result = (IntPtr)HTBOTTOMRIGHT;
handled = true;
}
}
if (!handled)
base.WndProc(ref m);
}
кстати, вы можете нарисовать системный размер окна с помощьюControlPaint.DrawSizeGrip Method
http://msdn.microsoft.com/en-us/library/2e1yx2sa.aspx
Антон Семенов, Я не понял ваш код.
в любом случае, у меня была проблема с первым код Чарльз Р,
когда я разворачиваю окно, а затем пытаюсь изменить его размер-он изменяется.
после этого я не мог исправить его снова до нормального размера, ни максимизировать его снова с помощью кнопки normal max.
что я сделал, чтобы исправить эту проблему, это добавление условия внутри цикла "foreach" в дно:
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 WM_MOUSEMOVE = 0x0200;
const UInt32 HTLEFT = 10;
const UInt32 HTRIGHT = 11;
const UInt32 HTBOTTOMRIGHT = 17;
const UInt32 HTBOTTOM = 15;
const UInt32 HTBOTTOMLEFT = 16;
const UInt32 HTTOP = 12;
const UInt32 HTTOPLEFT = 13;
const UInt32 HTTOPRIGHT = 14;
const int RESIZE_HANDLE_SIZE = 10;
bool handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
{HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
{HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
};
foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
{
if (this.WindowState != FormWindowState.Maximized
&& hitBox.Value.Contains(clientPoint))
{
m.Result = (IntPtr)hitBox.Key;
handled = true;
break;
}
}
}
if (!handled)
base.WndProc(ref m);
}
на основе Чарльза П. решение внесло некоторые изменения в него, надеюсь, что это поможет другим тоже :) Небольшие проверки и улучшения, чтобы не объявлять дополнительные переменные каждый раз при вызове сообщения windows. Также проверяет, не окрашивает ли захват якорь, когда состояние windows максимизировано. Я хотел создать пользовательский элемент управления из него, но, к сожалению, я закончил заполнение формы этим кодом.
конструктор или в файле конструктора:
this.DoubleBuffered = true;
this.ResizeRedraw = true;
переопределение окна функции:
const uint WM_NCHITTEST = 0x0084, WM_MOUSEMOVE = 0x0200,
HTLEFT = 10, HTRIGHT = 11, HTBOTTOMRIGHT = 17,
HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTTOP = 12,
HTTOPLEFT = 13, HTTOPRIGHT = 14;
Size formSize;
Point screenPoint;
Point clientPoint;
Dictionary<uint, Rectangle> boxes;
const int RHS = 10; // RESIZE_HANDLE_SIZE
bool handled;
protected override void WndProc(ref Message m)
{
if (this.WindowState == FormWindowState.Maximized)
{
base.WndProc(ref m);
return;
}
handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
formSize = this.Size;
screenPoint = new Point(m.LParam.ToInt32());
clientPoint = this.PointToClient(screenPoint);
boxes = new Dictionary<uint, Rectangle>() {
{HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RHS, RHS, RHS)},
{HTBOTTOM, new Rectangle(RHS, formSize.Height - RHS, formSize.Width - 2*RHS, RHS)},
{HTBOTTOMRIGHT, new Rectangle(formSize.Width - RHS, formSize.Height - RHS, RHS, RHS)},
{HTRIGHT, new Rectangle(formSize.Width - RHS, RHS, RHS, formSize.Height - 2*RHS)},
{HTTOPRIGHT, new Rectangle(formSize.Width - RHS, 0, RHS, RHS) },
{HTTOP, new Rectangle(RHS, 0, formSize.Width - 2*RHS, RHS) },
{HTTOPLEFT, new Rectangle(0, 0, RHS, RHS) },
{HTLEFT, new Rectangle(0, RHS, RHS, formSize.Height - 2*RHS) }
};
foreach (var hitBox in boxes)
{
if (hitBox.Value.Contains(clientPoint))
{
m.Result = (IntPtr)hitBox.Key;
handled = true;
break;
}
}
}
if (!handled)
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.WindowState != FormWindowState.Maximized)
{
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor,
this.ClientSize.Width - 16, this.ClientSize.Height - 16, 16, 16);
}
base.OnPaint(e);
}