Переопределить поведение изменения размера окна winform
У меня есть окно winform. Когда я меняю размер экрана, экран сразу увеличивается или уменьшается.
Я бы предпочел, чтобы поведение Resize окна было похоже на Split Container, пока я перетаскиваю мышь, я вижу только строку, которая отмечает размер окна, и только при выходе из операции Resize будет выполняться.
Я видел несколько примеров, которые показывают, что, скрывая рамку окна, а затем нажимая на само окно, рисует рамку.
Я хочу это, нажав на рамку окна (я не хочу скрывать рамку), а не на окне.
Есть какой-либо способ сделать это? (Может изменить поведение Resize любым способом).
1 ответ
Я уверен, что вы не можете найти никакого решения в Интернете. Однако я попробовал демо для этого, и это работает довольно хорошо.
В winforms
и многие другие технологии пользовательского интерфейса, вы не можете визуализировать что-либо за пределами самого окна. Чтобы получить желаемый эффект, мы должны визуализировать некоторую ориентировочную рамку снаружи или внутри окна, в зависимости от того, как пользователь ее изменяет. Похоже, мы застряли?
НО есть своего рода техника для этого (я называю это техникой слоя). Нам нужен прозрачный, не сфокусированный слой для визуализации ориентировочной границы. Этот слой будет иметь свой Size
а также Location
синхронизируется с Size
а также Location
(с небольшим смещением) главного окна. Слой также будет invisible
по умолчанию и отображается только при изменении размера пользователя и скрывается при завершении изменения размера.
Это довольно хорошо с техникой, которую я упомянул. Однако как предотвратить / отменить изменение размера по умолчанию, когда пользователь изменяет размер окна? К счастью, Win32
поддерживает 2 сообщения, чтобы сделать это легко:
- WM_RESIZING: отправляется в окно, когда пользователь запускает и продолжает изменять размер.
LParam
держитRECT
структура текущего окна при изменении размера. Мы читаем эту информацию, чтобы правильно отобразить ориентировочную границу. Тогда нам нужно изменить этоRECT
к текущемуBounds
окна, чтобы отменить эффект изменения размера по умолчанию (размер и местоположение изменяются немедленно). - WM_EXITSIZEMOVE: отправляется окну, когда заканчивается изменение размера или перемещение. Нам нужно поймать это сообщение, чтобы назначить
Size
а такжеLocation
окна на основеSize
а такжеLocation
прозрачного слоя и, конечно, скрыть слой тогда.
Теперь проблема полностью решаема. Вот демонстрационный код, который я сделал. Обратите внимание, что здесь есть очень неприятная неразрешимая и непонятная ошибка, это происходит, когда вы изменяете размер Top-Left
угол Size
обновляется правильно после отпускания мыши, но Location
устанавливается со смещением. Я отлаживаю, но не повезло. В какой-то момент Top
а также Left
переходит к неожиданным значениям без явной причины. Тем не менее, изменение размера по всем сторонам (слева, сверху, справа, снизу) и другим углам в порядке. На самом деле, изменение размера с помощью Top-Left corner
вряд ли это сделано пользователем, так что это решение приемлемо, я думаю.
//Must add using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Sizing border initialization
SizingBorderWidth = 3;
SizingBorderStyle = DashStyle.Custom;
SizingBorderColor = Color.Orange;
//layer initialization
layer.Owner = this;//especially this one.
layer.Width = Width + SizingBorderWidth * 2;
layer.Height = Height + SizingBorderWidth * 2;
//Paint the border when sizing
layer.Paint += (s, e) => {
using (Pen p = new Pen(SizingBorderColor) { Width = SizingBorderWidth }) {
if (Use3DSizingBorder) {
ControlPaint.DrawBorder3D(e.Graphics, sizingRect.Left, sizingRect.Top, sizingRect.Width, sizingRect.Height, Border3DStyle.Bump, Border3DSide.All);
}
else {
p.DashStyle = SizingBorderStyle;
p.LineJoin = LineJoin.Round;
if(p.DashStyle == DashStyle.Custom)
p.DashPattern = new float[] { 8f, 1f, 1f, 1f };//length of each dash from right to left
e.Graphics.DrawRectangle(p, sizingRect);
}
}
};
//Bind the Location of the main form and the layer form together
LocationChanged += (s, e) => {
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
//Set the intial Location of layer
Load += (s, e) =>{
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
}
//Set this to true to use 3D indicative/preview border
public bool Use3DSizingBorder { get; set; }
//Change the indicative/preview border thickness
public int SizingBorderWidth { get; set; }
//Change the indicative/preview border style
public DashStyle SizingBorderStyle { get; set; }
//Change the indicative/preview border color
public Color SizingBorderColor { get; set; }
//hold the current sizing Rectangle
Rectangle sizingRect;
bool startSizing;
bool suppressSizing;
//This is a Win32 RECT struct (don't use Rectangle)
public struct RECT
{
public int left, top, right, bottom;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x214&&!suppressSizing)//WM_SIZING = 0x214
{
RECT rect = (RECT) m.GetLParam(typeof(RECT));
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
sizingRect = new Rectangle() {X = SizingBorderWidth/2, Y = SizingBorderWidth/2,
Width = w, Height = h};
layer.Left = rect.left-SizingBorderWidth;
layer.Top = rect.top-SizingBorderWidth;
layer.Width = w+2*SizingBorderWidth;
layer.Height = h+2*SizingBorderWidth;
if (!startSizing)
{
layer.Show();
startSizing = true;
}
layer.Invalidate();
//Keep the current position and size fixed
rect.right = Right;
rect.bottom = Bottom;
rect.top = Top;
rect.left = Left;
//---------------------------
Marshal.StructureToPtr(rect, m.LParam, true);
}
if (m.Msg == 0x232)//WM_EXITSIZEMOVE = 0x232
{
layer.Visible = false;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = layer.Left + SizingBorderWidth;
Top = layer.Top + SizingBorderWidth;
Width = layer.Width - 2 * SizingBorderWidth;
Height = layer.Height - SizingBorderWidth * 2;
suppressSizing = false;
}));
startSizing = false;
}
base.WndProc(ref m);
}
//Here is the layer I mentioned before.
NoActivationForm layer = new NoActivationForm();
}
public class NoActivationForm : Form {
public NoActivationForm() {
//The following initialization is very important
TransparencyKey = BackColor;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
//----------------------------------------------
}
protected override bool ShowWithoutActivation {
get { return true; }
}
}
Некоторые скриншоты:
РЕДАКТИРОВАТЬ: (Это изменение было предложено Hodaya Shalom
ОП (странно:)
Я нашел решение проблемы левого угла:
перед BeginInvoke я сохраняю переменные и в вызове помещаю локальную переменную:
int _top = layer.Top + SizingBorderWidth;
int _left = layer.Left + SizingBorderWidth;
int _width = layer.Width - 2 * SizingBorderWidth;
int _height = layer.Height - SizingBorderWidth * 2;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = _left;
Top = _top;
Width =_width;
Height =_height;
suppressSizing = false;
}));