本人新手,想写个上位机,窗口能够实现最小化,最大化,拖动,改变大小等常用功能,Winform窗体的FormBorderStyle设置成sizable即可实现,但是窗体有自带的边框,不好看且无法编辑,想去掉,网上查到几种方法,都尝试了,但是都有缺陷,不太满意,不过最终还是实现了。走了弯路,浪费了不少时间,在这分享一下,最后效果是没有冗余边框,菜单栏在顶部
网上查到有两个方法(方法一和方法二),都需要将FormBorderStyle设置成None,首先说一下窗体的最小化最大化关闭,以及移动,然后是太多嵌套表格后防止闪烁,最后是窗体缩放,可以按目录自取。
目录
窗体最大化最小化和关闭
这个简单,直接放三个button,改背景图片,实现相应功能
private void buttonClose_Click(object sender, EventArgs e)
{
this.Close(); //关闭窗口
}
private void buttonMax_Click(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Maximized) //如果处于最大化,则还原
{
this.WindowState = FormWindowState.Normal;
Image backImage = Resources.最大化;
buttonMax.BackgroundImage = backImage;
}
else
{
this.WindowState = FormWindowState.Maximized; //如果处于普通状态,则最大化
Image backImage = Resources.还原;
buttonMax.BackgroundImage = backImage;
}
}
private void buttonMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized; //最小化
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState != FormWindowState.Maximized)
buttonMax.BackgroundImage = Resources.最大化;
}
最后的 Form1_SizeChanged的作用:当窗体最大化时,拖动窗体顶部窗体会变小,但是这种情况下最大化按钮的图标还是还原图标,所以写个方法刷新一下
点击最大化按钮后会发现窗体变大后覆盖了整个屏幕,把任务栏都遮住了,解决这个问题的方法如下:
//在窗体的Load事件中编写
private void Form1_Load(object sender, EventArgs e)
{
this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea;
}
窗体移动
我这里设置的是鼠标在菜单栏上按下时可以拖动,网上方法很多,下面这个是我认为最简单的
//窗体移动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Clicks == 1)
{
//窗体移动
if (e.Button == MouseButtons.Left)
{
ReleaseCapture(); //释放鼠标捕捉
//发送左键点击的消息至该窗体(标题栏)
SendMessage(this.Handle, 0xA1, 0x02, 0);
}
}
}
虽然不懂原理是啥,但是好用哈哈哈
参考文章:winform自定义最大化 - CSDNhttps://www.csdn.net/tags/MtTaAgzsNjk5ODYtYmxvZwO0O0OO0O0O.html
解决闪烁问题
为了让窗体在改变大小时各个部分能够更合理的自动分布,设计时用了很多表格,表格嵌套多了最大化最小化以及缩放窗体时就容易出现闪烁的情况,网上也有较多方法,下面这个是我试了之后有效果的
代码:
protected override CreateParams CreateParams //防止改变窗口大小时控件闪烁
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
补充说明:
FormBorderStyle设置成None,且添加了防闪烁代码后,最大化最小化窗口会显示不全,可以通过在Form1_SizeChanged方法中添加两个refersh()可以解决,但是视觉效果不好
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState != FormWindowState.Maximized)
buttonMax.BackgroundImage = Resources.最大化;
refresh();
refresh();
}
缩放
方法一
自己编写程序,主要通过MouseDown,MouseUp,MouseMove三个方法和自定义ResizeWindow方法实现根据鼠标位置判断和实施对应的窗体缩放。
//窗体改变大小
private bool isMouseDown = false; //表示鼠标当前是否处于按下状态,初始值为否
MouseDirection direction = MouseDirection.None; //表示拖动的方向,起始为None,表示不拖动
//private Point mPoint; //鼠标坐标
private bool changeornot = false; //是否是改变窗体大小,true为是,false为不是
//定义一个枚举,表示拖动方向,用于判断
public enum MouseDirection
{
HerizontalLeft, //水平方向拖动,只改变窗体的宽度
HerizontalRight,
VerticalTop, //垂直方向拖动,只改变窗体的高度
VerticalBottom,
DecliningLefttop, //倾斜方向,同时改变窗体的宽度和高度
DecliningLeftbottom,
DecliningRighttop,
DecliningRightbottom,
None //不做标志,即不拖动窗体改变大小
}
//鼠标在窗体内按下时获取鼠标的位置,并设置按下状态为真
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
//mPoint = new Point(e.X, e.Y);
isMouseDown = true;
}
// 鼠标弹起,不再改变窗体尺寸
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
direction = MouseDirection.None;
}
//鼠标移动过程中,坐标时刻在改变,根据不同位置改变拖动方向、鼠标样式和changeornot的状态
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown && direction != MouseDirection.None) //满足条件则直接执行改变窗口大小的方法
{
ResizeWindow();
return;
}
//当鼠标移动时横坐标距离窗体左边缘5像素以内且纵坐标距离上边缘也在5像素以内时
if (e.Location.X <= 5 && e.Location.Y <= 5)
{
this.Cursor = Cursors.SizeNWSE; //光标变为右下倾斜的箭头形状\
direction = MouseDirection.DecliningLefttop;
changeornot = true;
}
//左下
else if (e.Location.X <= 5 && e.Location.Y >= this.Height - 5)
{
this.Cursor = Cursors.SizeNESW; //光标变为右上倾斜的箭头形状/
direction = MouseDirection.DecliningLeftbottom;
changeornot = true;
}
//右上
else if (e.Location.X >= this.Width - 5 && e.Location.Y <= 5)
{
this.Cursor = Cursors.SizeNESW; //光标变为右上倾斜的箭头形状/
direction = MouseDirection.DecliningRighttop;
changeornot = true;
}
//右下
else if (e.Location.X >= this.Width - 5 && e.Location.Y > this.Height - 5)
{
this.Cursor = Cursors.SizeNWSE; //光标变为右下倾斜的箭头形状\
direction = MouseDirection.DecliningRightbottom;
changeornot = true;
}
//左
else if (e.Location.X <= 5)
{
this.Cursor = Cursors.SizeWE; //将光标变为水平的箭头形状
direction = MouseDirection.HerizontalLeft;
changeornot = true;
}
//右
else if (e.Location.X >= this.Width - 5)
{
this.Cursor = Cursors.SizeWE; //将光标变为水平的箭头形状
direction = MouseDirection.HerizontalRight;
changeornot = true;
}
//上
else if (e.Location.Y <= 1)
{
this.Cursor = Cursors.SizeNS; //将光标变为竖直的箭头形状
direction = MouseDirection.VerticalTop;
changeornot = true;
}
//下
else if (e.Location.Y >= this.Height - 5)
{
this.Cursor = Cursors.SizeNS; //将光标变为竖直的箭头形状
direction = MouseDirection.VerticalBottom;
changeornot = true;
}
//否则,其他窗体区域,鼠标星座均为单向箭头(默认)
else
{
this.Cursor = Cursors.Arrow;
direction = MouseDirection.None;
changeornot = false;
}
}
private void ResizeWindow()
{
//MousePosition的参考点是屏幕的左上角,表示鼠标当前相对于屏幕左上角的坐标。
//this.left和this.top的参考点也是屏幕,属性MousePosition是程序的重点
if (direction == MouseDirection.DecliningLefttop) //左上
{
this.Cursor = Cursors.SizeNWSE; //光标为右下倾斜的箭头形状\
this.Width = this.Right - MousePosition.X; //改变窗体宽和高
this.Height = this.Bottom - MousePosition.Y;
this.Location = new Point(MousePosition.X, MousePosition.Y); //改变窗体位置
}
else if (direction == MouseDirection.DecliningLeftbottom) //左下
{
this.Cursor = Cursors.SizeNESW;
this.Location = new Point(MousePosition.X, this.Top);
this.Width = this.Right - MousePosition.X;
this.Height = MousePosition.Y - this.Top;
}
else if (direction == MouseDirection.DecliningRighttop) //右上
{
this.Cursor = Cursors.SizeNESW;
this.Location = new Point(this.Left, MousePosition.Y);
this.Width = MousePosition.X - this.Left;
this.Height = this.Bottom - MousePosition.Y;
}
else if (direction == MouseDirection.DecliningRightbottom) //右下
{
this.Cursor = Cursors.SizeNWSE; //不改变窗体位置
this.Width = MousePosition.X - this.Left;
this.Height = MousePosition.Y - this.Top;
}
else if (direction == MouseDirection.HerizontalLeft) //左
{
this.Cursor = Cursors.SizeWE;
this.Location = new Point(MousePosition.X, this.Top);
this.Width = this.Right - MousePosition.X;
}
else if (direction == MouseDirection.HerizontalRight) //右
{
this.Cursor = Cursors.SizeWE;
this.Width = MousePosition.X - this.Left;
}
else if (direction == MouseDirection.VerticalTop) //上
{
this.Cursor = Cursors.SizeNS;
this.Location = new Point(this.Left, MousePosition.Y);
this.Height = this.Bottom - MousePosition.Y;
}
else if (direction == MouseDirection.VerticalBottom) //下
{
this.Cursor = Cursors.SizeNS;
this.Height = MousePosition.Y - this.Top;
}
//即使鼠标按下,但是不在窗口右和下边缘,那么也不能改变窗口大小
else
{
this.Cursor = Cursors.Arrow;
}
}
实际效果是能缩放,但是闪烁卡顿严重
winform 设置无边框后的窗口大小调整 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/505185678
方法二
直接上代码
//窗体缩放
const int Guying_HTLEFT = 10;
const int Guying_HTRIGHT = 11;
const int Guying_HTTOP = 12;
const int Guying_HTTOPLEFT = 13;
const int Guying_HTTOPRIGHT = 14;
const int Guying_HTBOTTOM = 15;
const int Guying_HTBOTTOMLEFT = 0x10;
const int Guying_HTBOTTOMRIGHT = 17;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0084:
base.WndProc(ref m);
Point vPoint = new Point((int)m.LParam & 0xFFFF,
(int)m.LParam >> 16 & 0xFFFF);
vPoint = PointToClient(vPoint);
if (vPoint.X <= 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)Guying_HTTOPLEFT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOMLEFT;
else m.Result = (IntPtr)Guying_HTLEFT;
else if (vPoint.X >= ClientSize.Width - 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)Guying_HTTOPRIGHT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOMRIGHT;
else m.Result = (IntPtr)Guying_HTRIGHT;
else if (vPoint.Y <= 2)
m.Result = (IntPtr)Guying_HTTOP;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOM;
break;
default:
base.WndProc(ref m);
break;
}
}
其方法与一类似,通过判断鼠标位置实施不同缩放,但是缩放的方法不是自己写的,这个方法缩放会流畅很多,和有边框的窗口差不多,但是因为FormBorderStyle设置成None,没有边框,缩放过程中部分控件又会轻微闪烁(有防闪烁代码的基础上)。一般使用边框附近5个像素,因为我的菜单栏距顶部只有两个像素,设置成5会失效,所以上方缩放我设置的2 (else if (vPoint.Y <= 2))
Winform自定义无边框窗体 - landptf - 博客园 (cnblogs.com)https://www.cnblogs.com/landptf/p/5068523.html
方法三
设置FormBorderStyle设置成Sizable,但是窗口样式中controlbox设置成false,且Form1的Text设置为空,效果如下。这样能够拥有边框的缩放功能,没有一点卡顿和闪烁,但是顶端有一个白色窄边,其他三边没有,但是最大化窗口时其他三边会有缝隙,也就是说边框虽然不显示,但还是在。
方法四
此方法可以说是完美解决问题,经过方法二和方法三的折腾,我发现可以将窗口的FormBorderStyle设置成FixedSingle(3D也行,看个人审美),然后controlbox设置成false,且Text设置为空,此时窗口拥有系统的边框,不占位置,能够使用系统自带窗体的功能(比如点击任务栏图标可以最小化和还原),且显示正常(最大化最小化不会有显示缺陷),但是窗口不能缩放,再将方法二的代码加上,就完美解决了
适合新手使用
完整代码(需要按照方法四设置窗体)
#region 窗体设置
private void buttonClose_Click(object sender, EventArgs e)
{
this.Close(); //关闭窗口
}
private void buttonMax_Click(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Maximized) //如果处于最大化,则还原
{
this.WindowState = FormWindowState.Normal;
Image backImage = Resources.最大化;
buttonMax.BackgroundImage = backImage;
}
else
{
this.WindowState = FormWindowState.Maximized; //如果处于普通状态,则最大化
Image backImage = Resources.还原;
buttonMax.BackgroundImage = backImage;
}
}
private void buttonMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized; //最小化
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState != FormWindowState.Maximized)
buttonMax.BackgroundImage = Resources.最大化;
}
protected override CreateParams CreateParams //防止改变窗口大小时控件闪烁
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
//窗体移动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Clicks == 1)
{
//窗体移动
if (e.Button == MouseButtons.Left)
{
ReleaseCapture(); //释放鼠标捕捉
//发送左键点击的消息至该窗体(标题栏)
SendMessage(this.Handle, 0xA1, 0x02, 0);
}
}
}
//窗体缩放
const int Guying_HTLEFT = 10;
const int Guying_HTRIGHT = 11;
const int Guying_HTTOP = 12;
const int Guying_HTTOPLEFT = 13;
const int Guying_HTTOPRIGHT = 14;
const int Guying_HTBOTTOM = 15;
const int Guying_HTBOTTOMLEFT = 0x10;
const int Guying_HTBOTTOMRIGHT = 17;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0084:
base.WndProc(ref m);
Point vPoint = new Point((int)m.LParam & 0xFFFF,
(int)m.LParam >> 16 & 0xFFFF);
vPoint = PointToClient(vPoint);
if (this.WindowState != FormWindowState.Normal)
break; //源代码在窗体最大化状态下,鼠标移到窗口边缘也会出现拖动标识,添加这句代码可以避免
if (vPoint.X <= 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)Guying_HTTOPLEFT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOMLEFT;
else m.Result = (IntPtr)Guying_HTLEFT;
else if (vPoint.X >= ClientSize.Width - 5)
if (vPoint.Y <= 5)
m.Result = (IntPtr)Guying_HTTOPRIGHT;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOMRIGHT;
else m.Result = (IntPtr)Guying_HTRIGHT;
else if (vPoint.Y <= 2)
m.Result = (IntPtr)Guying_HTTOP;
else if (vPoint.Y >= ClientSize.Height - 5)
m.Result = (IntPtr)Guying_HTBOTTOM;
break;
default:
base.WndProc(ref m);
break;
}
}
#endregion
——————更新一下(2023.03):
更新一下:
1.实践发现protected override CreateParams CreateParams这段防闪烁的代码实际没有效果,可以去掉,有fixed边框后就几乎没有闪烁了
2. Form1_Load事件中添加的this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea这段代码会导致窗口在扩展屏上最大化时显示错误(我遇到的情况是最大化未占满整个屏幕),可以去掉,然后在buttonMax_Click事件中添加如下代码
this.MaximumSize = Screen.FromHandle(this.Handle).WorkingArea.Size。
直接将this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea搬运到buttonMax_Click事件中是不行的,可能是因为窗体在加载的时候获取了屏幕大小,然后就将其值固定不变了,只改变显示区域不会改变最大化的尺寸,因此需要在buttonMax_Click事件中获取当前屏幕工作区的尺寸并赋值给窗体最大化尺寸。
3.要比较容易的缩放窗体,需要窗体预留足够宽度的margin(或者是padding,具体我忘了,反正就是窗体边缘的几个像素要留出来)因为系统识别的窗体边缘的像素,如果用其他控件遮挡了窗体的边缘,鼠标移到窗体边缘时就检测不到,结果就是鼠标滑动很久才能勉强找到可以缩放的点,轻微移动缩放标识就会消失。