【WinForm】运行时模仿窗体设计调整控件大小和位置
分析
我们先来看看,窗体设计时的控件样式
点击控件时会显示4条虚线和8个小矩形,但是同时我们为了绘制虚线和矩形,需要多设置4条底边
移动控件时会显示4条实线
实现运行时调整控件大小和位置,只需为控件绑定MouseDown,MouseClick,MouseMove,MouseUp事件即可
MouseDown:鼠标键按下时,显示灰色实线
MouseClick:鼠标单击时,显示4条虚线和8个小矩形
MouseMove:鼠标拖动控件时,显示灰色实线,当鼠标点击控件时也会执行该事件
MouseUp:鼠标键释放时,显示4条虚线和8个小矩形
我们可以用GDI+绘图技术,绘制控件的灰色实线和虚线
我们先制作边框控件,只需绑定MouseDown,MouseMove,MouseUp事件即可
MouseDown:鼠标键按下时,记录当前鼠标位置
MouseMove:鼠标在控件上移动时,移动时刷新4条灰色实线,改变鼠标光标并调整控件大小
MouseUp:鼠标键释放时,刷新4条虚线
边框控件包含边框的4条底边,4条灰色实线,4条虚线和8个小矩形
制作边框控件
边框控件首先需要继承UserControl
绑定MouseDown,MouseMove,MouseUp事件
接下来编写几个重要的方法
- 绘制实线:4条实线,移动控件和调整控件大小时显示
- 绘制虚线:包含4条底边,4条虚线和8个小矩形,点击控件后显示
- 改变鼠标状态:鼠标在控件上,下,左,右,左上,右上,左下,右下,向上不同的光标
- 调整控件大小:鼠标在控件中的不同位置,调整控件大小
绘制实线
绘制实线前,需要将隐藏边框(即是4条底边,4条虚线和8个小矩形)
public void DrawSolids()
{
Visible = false;
Graphics g = control.CreateGraphics();
int width = control.Width;
int height = control.Height;
Point[] points = new Point[5] { new Point(0,0),new Point(width - 1,0),
new Point(width - 1,height-1),new Point(0,height-1),new Point(0,0)};
g.DrawLines(new Pen(Color.Gray, 3), points);
}
绘制虚线
绘制虚线前,需要将显示边框,在绘制4条底边,4条虚线和8个小矩形
public void DrawDottedLines()
{
Visible = true;
#region 设置控件区域
BringToFront();
int x = control.Bounds.X - smallRectSize.Width;
int y = control.Bounds.Y - smallRectSize.Height;
int width = control.Bounds.Width + (smallRectSize.Width * 2);
int height = control.Bounds.Height + (smallRectSize.Height * 2);
Bounds = new Rectangle(x, y, width, height);
//包括边框的局域
controlRect = new Rectangle(new Point(0, 0), Bounds.Size);
Graphics g = CreateGraphics();
g.Clear(control.Parent.BackColor);//清除之前绘制的
#endregion
#region 4条底边
GraphicsPath path = new GraphicsPath();
//上底边
borderRects[0] = new Rectangle(0, 0, Width + size * 2, smallRectSize.Height + 1);
//左底边
borderRects[1] = new Rectangle(0, size + 1, smallRectSize.Width + 1, Height - size * 2 - 2);
//下底边
borderRects[2] = new Rectangle(0, Height - size - 1, Width + size * 2, smallRectSize.Height + 1);
//右底边
borderRects[3] = new Rectangle(Width - size - 1, size + 1, smallRectSize.Width + 1, Height - size * 2 - 2);
path.AddRectangle(borderRects[0]);
path.AddRectangle(borderRects[1]);
path.AddRectangle(borderRects[2]);
path.AddRectangle(borderRects[3]);
Region = new Region(path);
#endregion
#region 4条虚线
//左上
linePoints[0] = new Point(3, 3);
//右上
linePoints[1] = new Point(Width - 3 - 1, 3);
//右下
linePoints[2] = new Point(Width - 3 - 1, Height - 3 - 1);
//左下
linePoints[3] = new Point(3, Height - 3 - 1);
//左上
linePoints[4] = new Point(3, 3);
Pen pen = new Pen(Color.Black, 1) { DashStyle = DashStyle.Dot };
g.DrawLines(pen, linePoints);
#endregion
#region 8个小矩形
//左上
smallRects[0] = new Rectangle(new Point(0, 0), smallRectSize);
//右上
smallRects[1] = new Rectangle(new Point(Width - size - 1, 0), smallRectSize);
//左下
smallRects[2] = new Rectangle(new Point(0, Height - size - 1), smallRectSize);
//右下
smallRects[3] = new Rectangle(new Point(Width - size - 1, Height - size - 1), smallRectSize);
//上中
smallRects[4] = new Rectangle(new Point(Width / 2 - 1, 0), smallRectSize);
//下中
smallRects[5] = new Rectangle(new Point(Width / 2 - 1, Height - size - 1), smallRectSize);
//左中
smallRects[6] = new Rectangle(new Point(0, Height / 2 - size / 2), smallRectSize);
//右中
smallRects[7] = new Rectangle(new Point(Width - size - 1, Height / 2 - size / 2), smallRectSize);
//填充矩形内部为白色
g.FillRectangles(Brushes.White, smallRects);
//绘制矩形
g.DrawRectangles(Pens.Black, smallRects);
#endregion
}
改变鼠标状态
先定义一个枚举,存储鼠标在控件中的位置
enum MousePos
{
None,
Top,
Right,
Bottom,
Left,
LeftTop,
LeftBottom,
RightTop,
RightBottom
}
根据鼠标在控件中的不同位置,改变鼠标状态
private void SetCursor(int x, int y)
{
Point point = new Point(x, y);
if (!controlRect.Contains(point))//不在边框局域内直接退出
{
Cursor.Current = Cursors.Arrow;
return;
}
else
if (smallRects[0].Contains(point))//左上
{
Cursor.Current = Cursors.SizeNWSE;
mousePos = MousePos.LeftTop;
}
else if (smallRects[1].Contains(point))//右上
{
Cursor.Current = Cursors.SizeNESW;
mousePos = MousePos.RightTop;
}
else if (smallRects[2].Contains(point))//左下
{
Cursor.Current = Cursors.SizeNESW;
mousePos = MousePos.LeftBottom;
}
else if (smallRects[3].Contains(point))//右下
{
Cursor.Current = Cursors.SizeNWSE;
mousePos = MousePos.RightBottom;
}
else if (borderRects[0].Contains(point))//上
{
Cursor.Current = Cursors.SizeNS;
mousePos = MousePos.Top;
}
else if (borderRects[1].Contains(point))//左
{
Cursor.Current = Cursors.SizeWE;
mousePos = MousePos.Left;
}
else if (borderRects[2].Contains(point))//下
{
Cursor.Current = Cursors.SizeNS;
mousePos = MousePos.Bottom;
}
else if (borderRects[3].Contains(point))//右
{
Cursor.Current = Cursors.SizeWE;
mousePos = MousePos.Right;
}
else
{
Cursor.Current = Cursors.Arrow;
}
}
调整控件大小
比如,当鼠标处于控件的下方时,调整控件的Height属性即可
写成扩展方法,方便使用
public static partial class Extensions
{
/// <summary>
/// 为控件绑定事件
/// </summary>
/// <param name="control"></param>
public static void SetMove(this Control control)
{
FrameControl fControl = null;//边框控件
Point lPoint = new Point();//上一个鼠标坐标
Point cPoint = new Point();//当前鼠标坐标
#region 绑定事件
//鼠标键按下时
control.MouseDown += (sender, e) =>
{
//记录当前鼠标坐标
lPoint = Cursor.Position;
//清除所有控件的边框区域,最主要的是清除上次点击控件的边框,恢复原来状态
foreach (Control ctrl in control.Parent.Controls)
if (ctrl is FrameControl)
ctrl.Visible = false;
fControl = new FrameControl(control);
control.Parent.Controls.Add(fControl);
};
//鼠标单击时
control.MouseClick += (sender, e) =>
{
control.BringToFront();
};
//鼠标在控件上移动时
control.MouseMove += (sender, e) =>
{
Cursor.Current = Cursors.SizeAll;
if (e.Button == MouseButtons.Left)
{
cPoint = Cursor.Position;
control.Location = new Point(control.Location.X + cPoint.X - lPoint.X,
control.Location.Y + cPoint.Y - lPoint.Y);
//移动时刷新实线
fControl.DrawSolids();
control.BringToFront();
}
lPoint = cPoint;
};
//鼠标键释放时
control.MouseUp += (sender, e) =>
{
//显示虚线
fControl.DrawDottedLines();
};
#endregion
}
}
实现很简单,只需一句代码
button1.SetMove();
在运行时的效果
本文参考了网上的一些资料,自己并做了调整,使在运行时调整控件大小更像窗体设计的
但同时存在一个小问题,就是在调整控件大小时,鼠标光标会变成默认状态,虽然问题不大,但是也是个问题
源码下载:
上一篇: bootstrap table可编辑
下一篇: el-tree 修改选中状态背景色