自适应窗口布局:OnLayout()
窗口在尺寸变化时,会调用OnLayout方法, 所以 , 在form1.cs文件里重写OnLayout方法即可实现自适应
在OnLayout方法中,我们获取一下窗口的宽度和高度,根据窗口的宽度和高度重新设定控件的位置和尺寸
注意: 1. Size是指整个窗口的大小, ClientSize是窗口的用户区域大小
2.有些控件的尺寸是被锁住的,比如textBox的高度。
效果展示:
窗口里的三个控件都随着界面改变位置和大小
//窗体随页面拉动变大变小
protected override void OnLayout (LayoutEventArgs levent){
//1.调父类的onLayout(),不是必需的
base. OnLayout (levent) ;
//2.获取窗口大小clientsize(仅客户区,不含标题栏)
int w = this. ClientSize. Width;
int h = this. ClientSize. Height;
//3.计算每-一个控件的位置和大小
int yoff = 0;
yoff = 4;
this. textBox1. Location = new Point(0,yoff);|
this. textBoxl.Size = new Size(w-80,40) ;
this. button1. Location = new Point(w - 80,yoff) ;
this. button1.Size = new Size(80,40) ;
yoff += 40;//第一行的高度
yoff += 4;//间隔
this. pictureBoxl. Location = new Point(0, yoff) ;
this. pictureBoxl.Size = new Size(w,h - yoff - 4) ;
}
2. 锚点固定
每个控件都有一个通用属性Anchor (锚点)
也可以实现拉伸的效果:
也可以实现居中效果:
也可以实现水平竖直同时居中:
也可以实现平铺效果:
3. Dock栏
用dock方式设置了一个典型的布局
注意:锚点Anchor和Dock不能同时设置,设置Dock属性后,Anchor属性就会失效
4.自定义布局器:LayoutEngine()
布局器LayoutEngine :负责子控件的布局
默认地,一个Form或 Panel 都自带了一个布局器
在窗口改变大小时,由窗口的布局器来负责调整布局
自己编写一个布局规则,在也页面设计时,拉过来就能使用了
5.流式布局:FlowLayoutPanel
在流式布局容器里的控件是按规律一行排序,排满才换行
控件未知会随容器变化而改变位置
winform自带了流式布局面板,拖入即可用
注意:flowLayoutPanel至少一个面板,它并不是一个布局器,但是它内部默认实现了一个布局器
6.表格布局:TableLayoutPanel
winform自带了表格布局面板,拖入即可用
右键添加行列
也可以在属性里设置
点击这个按钮可以打开行列的具体设置
7.DockLayout
DockLayout:是对默认Dock布局的优化版
需要自己定义一个控件(我还不会)
DockLayout的特点:
1支持上、下、左、右、中位置,顺序无关
2支持各个控件的Margin
3支持设置容器本身的Padding
8.常用的控件
8.1 TextBox 文本框控件
想要设置宽高时,需要在cs文件中的click控制方法中编写:
textBox.AutoSize = false;//也可以在属性栏中设置,当为true时,文本框会自己计算所需大小
textBox.Height = 40;
属性
设计
Name:变量名
外观
Text:文本
Font:字体
事件
KeyPress:按键事件,常用于文本框输入完成后,用户回车触发处理
private void TestKeyPress(object sender, KeyPressEventArgs e)
{
char keyChar = e.KeyChar;
if (keyChar == '\r')
{
string text = textBox1.Text;
MessageBox.Show(text, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
8.2 CheckBox 复选框
属性:
外观:
Text : 文本显示
Checked : √/否
事件:
Click:点击动作
CheckedChanged :选中状态发生变化
练习:复选框显示密码选择,当勾选时,密码可见,未勾选时密码隐藏
使用这个
//复选框状态变化世家
private void TestPassWordCheckChanged(object sender, EventArgs e)
{
bool che = checkBox1.Checked;
if (che)
{
textBox1.PasswordChar = '\0';
}
else {
textBox1.PasswordChar = '*';
}
}
注意:
区分Click和CheckedChanged两种事件
Click:用户手动点击
CheckedChanged :状态值发生变化,可能是用户点击,也可能是程序代码改变了这个值
例如:
checkBox1.Checked = true;
8.3 comboBox 下拉列表
1.添加数据项
在设计器里直接编辑
属性|数据|Items
也可以在构造方法里手工添加
public Form1()
{
InitializeComponent();
//注意:必须写在这个类的构造方法这个位置才会生效
comboBox1.Items.Clear();
comboBox1.Items.Add("张三");
comboBox1.Items.Add("李四");
comboBox1.Items.Add("王五");
comboBox1.SelectedIndex = 0;//默认选中下标为0的选项
}
2.获取选中的项
SelectedItem :选中项的值,null表示未选中
SelectedIndex :选中项的索引,-1表示未选中
//选中后,点击按钮触发按钮绑定的事件,编译模式下控制台输出,选中的选项
private void button1_ Click (object sender,EventArgs e)
{
// selectedItem :选中项的值,null表示未选中
// selectedIndex :选中项的索引,-1表示未选中
int index = comboBox1. SelectedIndex;
string sel = (string) comboBox1. SelectedItem;
Console. WriteLine ("选中了:”+ sel) ;
}
comboBox1.SelectedIndex = 0;//默认选中下标为0的选项
3.事件
事件|行为| SelectedIndexChanged
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Console.WriteLine("现在选中的是"+comboBox1.SelectedItem);
}
8.4 ListBox列表框
属性:
(行为) SelectionMode:单选/多选
事件:
(行为) SelectedIndexChanged:选项操作事件
8.5 (练习)学生信息编辑
练习:实现一个学生信息的编辑器-学号,姓名,性别,手机号
将数据保存到文件
启动时从文件读取
1界面布局
添加需要的控件.修改显示文本Text-手工对齐
修改控件名id、Name、sex、number
2保存功能
点保存时,将界面的数据保存到文件中-添加NewtonSoft.json支持
添加按钮事件处理
将数据保存为JSON,存到文件中
3加载功能
当程序启动时,自动读取student.txt中的数据
在构造方法中加载
读取文件,转成JSON
将数据显示到界面
8.6 CheckListBox 多选框
简介类似ListBox项的列表区别:每个项前面有个复选框
事件:
CheckOnClick() :设置true,点击时会选中或者取消复选框
属性:
Items : 复选框中各项的集合
方法 :
checkedListBox1.Items.Add() :添加复选框的选项
DataSource DisplayMember ValueMember4.事件: SelectedIndexChangedItemCheck
9.图片
10.启动后台工作支线程:backgroundWorker
我们在项目中,应该经常需要进行多线程操作,比如需要开辟一个线程去执行很耗时的任务。如果不使用多线程,那么页面就会被卡死(主线程运行时,界面无法拖动,知道界面运行完毕,界面才可以拖动)。
属性:
WorkerReportsProgress:用于设置后台任务是否可以把它的进度汇报给主线程
WorkerSupportsCancellation:是否支持从主线程取消
IsBusy:检查后台任务是否正在运行
事件:
DoWork:在后台线程开始的时候触发DoWork。DoWork事件处理程序(在后台线程)
在希望向主线程汇报进度的时候,调用 backgroundWorker1.ReportProgress(i);方法。
ProgressChanged:在后台任务汇报状态的时候触发ProgressChanged事件
RunWorkerCompleted:后台工作线程退出的时候触发RunWorkerCompleted事件
用来发送不同的程序事件和状态。你需要为自己的程序写这些事件的事件处理方法来执行适合程序的行为;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//开启跨线程修改UI界面(不推荐使用,不安全),默认为true
//CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "开始")
{
backgroundWorker1.RunWorkerAsync();
button1.Enabled = false;
}
}
//1、开启异步后台运行时,自动做的工作
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 1; i < 101; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
//方法一:
//使用Invoke方式跨线程直接修改UI界面
//Invoke(() => {
// progressBar1.Value = i;
//});
//方法二:
//进程改变处理函数,汇报进度(以供backgroundWorker1_ProgressChanged()时间获取后台DoWork进度)
backgroundWorker1.ReportProgress(i);
}
}
//1、获取后台汇报的进度,在主线程中执行
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;//获取后台工作汇报的进度
}
//3、后台工作运行结束后开启的事件
private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
MessageBox.Show("后台工作执行完毕");
button1.Enabled = true;
progressBar1.Value = 1;
}
}
11、DataGripView : 数据单元格
public partial class Form2 : Form{
public Form2(){
InitializeComponent();
//一次选择一整行
dgv1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
//去掉表格最下面的空白行
dgv1.AllowUserToAddRows = false;
//设置是否可以多选
dgv1.MultiSelect = false;
//行头是否隐藏
dgv1.RowHeadersVisible = false;
var style = dgv1.RowsDefaultCellStyle;
//行的背景颜色
style.BackColor = Color.AliceBlue;
//行的字体颜色
style.ForeColor = Color.Blue;
//设置选中行的背景颜色
style.SelectionBackColor = Color.Red;
}
private void Form2_Load(object sender, EventArgs e){
dgv1.Columns.Add("id", "编号");
dgv1.Columns.Add("name", "姓名");
dgv1.Columns.Add("age", "年龄");
dgv1.Columns.Add("sex", "性别");
for (int i = 0; i <= 100; i++) {
if (i == 0){
dgv1.Rows.Add(i, "王一", 20, "男");
}
else if (i % 2 == 0){
dgv1.Rows.Add(i, "李二", 21, "男");
}
else if (i % 2 == 1) {
dgv1.Rows.Add(i, "刘思思", 18, "女");
}
}
}
private void dgv1_SelectionChanged(object sender, EventArgs e){
DataGridViewSelectedRowCollection srs = dgv1.SelectedRows;
DataGridViewRow row = srs[0];
if (row != null){
textBox1.Text = row.Cells[0].Value.ToString();
textBox2.Text = row.Cells[1].Value.ToString();
textBox3.Text = row.Cells[2].Value.ToString();
textBox4.Text = row.Cells[3].Value.ToString();
}
}
}
11、无边框窗体拖动方法
给窗体中头部的控件绑定这两个事件,即可实现鼠标点击头部拖动控件
//无边框窗体拖动
#region 方法一 (需要在主窗体的设置拖动窗体控件的MouseDown和MouseMove事件绑定下面两个方法)
//记录当前窗体的位置
private Point point;
private void MouseDown(object sender, MouseEventArgs e)
{
//获取当前窗体的位置,存放到point变量中
point = new Point(e.X, e.Y);
}
private void MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point myPoint = MousePosition;
//偏移量
myPoint.Offset(-point.X, -point.Y);
//设置当前床日实时移动的位置
DesktopLocation = myPoint;
}
}
#endregion
#region 方法二(在主窗体的Load事件中调用这个方法即可)
/// <summary>
/// 移动控件
/// </summary>
/// <param name="con">接收鼠标的事件控件</param>
/// <param name="conMove">需要移动的控件</param>
public static void SetMove(Control con, Control conMove)
{
int x = 0; //按下坐标X
int y = 0; //按下坐标Y
bool isDown = false; //鼠标是否按下
//鼠标按下顶部控件时发生
con.MouseDown += (s, e) =>
{
con.Cursor = Cursors.SizeAll; //鼠标外观
x = e.X;
y = e.Y;
isDown = true;
};
//鼠标抬起顶部控件时发生
con.MouseUp += (s, e) =>
{
con.Cursor = Cursors.Default;
isDown = false;
};
//鼠标移动顶部控件时发生
con.MouseMove += (s, e) =>
{
if(isDown)
{
//修改主窗体的位置
conMove.Location = new System.Drawing.Point(conMove.Left + (e.X - x), conMove.Top + (e.Y - y));
}
};
}
#endregion
12、点击按钮在本窗体展示多个子窗体
使用反射
//反射方法类
namespace TestShowForm
{
internal class ShowFormUtil
{
public static UserForm userForm(string param)
{
UserForm form = new UserForm();
//form.DbParam = param;
return form;
}
public static RoleForm roleForm(string param)
{
return new RoleForm();
}
public static TokenForm tokenForm(string param)
{
return new TokenForm();
}
}
}
//按钮点击
private void btn1_Click(object sender, EventArgs e)
{
var controls = fpanelLeft.Controls;
foreach (Control con in controls)
{
if (con is Button btn)
{
btn.BackColor = Color.Aqua;
}
}
Button button = sender as Button;
string name =(string)button.Tag;
Type type = typeof(ShowFormUtil);
MethodInfo me = type.GetMethod(name);
if (me == null)
{
return;
}
button.BackColor = Color.Red;
object fo = me.Invoke(null, new object[] { name });
ParentForm sonForm = fo as ParentForm;
if (panelMain.Controls.Count > 0)
{
panelMain.Controls.Clear();
}
sonForm.TopLevel = false;
sonForm.FormBorderStyle = FormBorderStyle.None;
sonForm.WindowState = FormWindowState.Maximized;
panelMain.Controls.Add(sonForm);
sonForm.Show();
}
private void Form1_Load(object sender, EventArgs e)
{
//伪造打开窗体的方法名(可以从数据库中读取数据,自动创建这些按钮),用来使用反射调用该方法
string[] formName = {"userForm","roleForm","tokenForm" };
foreach (string name in formName)
{
//创建按钮
Button btn = new Button();
btn.Tag = name;
btn.Text = name;
btn.Click += btn1_Click;
//fpanelLeft:流式布局控件(放置按钮控件的父控件)
btn.Width = fpanelLeft.Width-6;
btn.Height = 30;
btn.FlatAppearance.BorderSize = 0;
btn.FlatStyle = FlatStyle.Popup;
btn.BackColor = Color.Aqua;
fpanelLeft.Controls.Add(btn);
}
}
普通展示
#region 显示菜单按钮和窗体
/// <summary>
/// 显示菜单按钮和窗体
/// </summary>
/// <typeparam name="ToolStripMain"></typeparam>
public void ShowMenuAndForm<T>(string name) where T : Form,new()
{
//判断窗体或者菜单按钮存在不存在
//======获取所有菜单按钮
var items = ToolStripMain.Items;
T form =null;
ToolStripButton button = null;
//找到按钮
//======遍历所有菜单按钮,找到与窗体绑定的下拉菜单选项的按钮的Name相同的菜单按钮
foreach (ToolStripItem item in items)
{
if (item.Name == name)
{
button = item as ToolStripButton;
}
}
//找到窗体
Form[] sonForm = this.MdiChildren;
//======遍历这个窗体下的所有子窗体,找到与菜单按钮相同Name的窗体
foreach (Form f in sonForm)
{
if (f.Name == name)
{
form = f as T;
}
}
if (form != null && button != null) {
//窗体最大化
form.WindowState = FormWindowState.Maximized;
//菜单按钮点击把底下覆盖的窗体置顶
form.BringToFront();
//遍历所有菜单按钮,把按钮的背景颜色去掉
foreach (ToolStripButton item in items)
{
item.BackColor = SystemColors.Window;
}
//点击时给选中的菜单按钮设置背景
button.BackColor = Color.LightBlue;
return;
}
//添加窗体
form = new T();
//设置人员窗体的父窗体是当前主窗体
form.MdiParent = this;
form.Name = name;
form.WindowState = FormWindowState.Maximized;
//添加菜单按钮
button = new ToolStripButton();
button.Text = form.Text;
button.Name = name;
//遍历所有菜单按钮,把按钮的背景颜色去掉
foreach (ToolStripButton item in items)
{
item.BackColor = SystemColors.Window;
}
button.BackColor = Color.LightBlue;
//把添加的一个菜单按钮,添加到菜单按钮的集合中去
ToolStripMain.Items.Add(button);
button.MouseDown += (s, e) =>
{
if(e.Button == MouseButtons.Left) //切换
{
//窗体最大化
form.WindowState = FormWindowState.Maximized;
//菜单按钮点击把底下覆盖的窗体置顶
form.BringToFront();
//遍历所有菜单按钮,把按钮的背景颜色去掉
foreach (ToolStripButton item in items)
{
item.BackColor = SystemColors.Window;
}
//点击时给选中的菜单按钮设置背景
button.BackColor = Color.LightBlue;
}
else if (e.Button == MouseButtons.Right) //删除
{
DialogResult dr = BoxUtil.OKCancel("是否关闭当前窗口");
if (dr == DialogResult.OK) {
form.Close();
}
}
};
form.FormClosing += (s, e) =>
{
ToolStripMain.Items.Remove(button);
};
form.Show();
}
#endregion