一、项目创建
- 菜单栏上的【文件】-【新建】-【项目】;
- 在新建项目窗口中,左菜单上选择编程语言Vistual C#,右菜单上选择Windows 窗体应用(.NET Framework);
- 在下方设置好对应的项目名(标识符,即由字母、数字、下划线组成)、保存路径。
二、项目结构
App.config 应用配置
Form1.cs 源码文件(窗口)
Form1.Designer.cs 源码文件(界面设计)业务逻辑
Form.resx 资源文件
Program.cs 源码文件(Main方法)由系统自动生成
类的拆分:
Form1:Form1.cs业务代码+Form.Designer.cs界面代码,自动生成(一般不需要手工修改)
三、基本操作
(一)窗口
1. 自定义窗口——以MyForm为例
- 删除默认创建的整个form1.cs
- 选中项目-右击-添加【类】
- 选中【Visual C#-类 】,并进行命名
- 修改Form1.cs与Program.cs中的代码
Form1.cs→MyForm.cs:
using System.Windows.Forms;
namespace Form_1
{
/*新建一个自定义窗口——MyForm*/
class MyForm: Form//MyForm继承Form,使用Alt+Enter将“using System.Windows.Forms;”填充到上方
{
public MyForm()//构造MyForm方法
{
this.Text = "我的小窗口";//设置窗口标题
}
}
}
Program.cs:
namespace Form_1
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]//attribute属性,相当于java里的注解;对Main方法来进行修饰,用来设置一些运行时候的属性
/*将自定义的窗口显示出来,将原有的多余部分删除“Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);”*/
static void Main()
{
MyForm form = new MyForm();//创建MyForm对象,并将该方法教给application.Run()
Application.Run(form);//启动消息循环
}//或者也可以写成: MyForm form; Application.Run(new MyForm());
}
}
(二)添加控件
2.1 添加控件原理
代码之间的调用关系:Form1→InitializeComponent()(放置在Form.Designer.cs文件中) 即在Form.cs中调用InitializeComponent()方法
2.2 手动添加控件
一般都是在界面设计器中添加,有时候使用手动添加
public partial class Form1 : Form
{
private Button textButton = new Button();//创建一个Button对象
public Form1()
{
InitializeComponent();//界面初始化,在它之后添加自己的代码
this.Controls.Add(textButton);//放置到窗口中
/*设置Button的属性*/
textButton.Text = "我的测试";
textButton.Location = new Point(40, 40);//位置,(x,y)
textButton.Size = new Size(100,40);//大小,(width,heigh)
}
}
(三)事件处理
3.1 自动添加
进入控件的属性面板,在属性面板的事件名后写入方法名(自定义方法名)后回车/直接双击
直接双击控件,添加默认事件和默认方法名不推荐
要点与细节:
- 事件处理回调定义在Form.cs中;
- MessageBox.Show()——弹出一个消息框;
- 在GUI程序中,控制台输出不起作用;在调试状态下Console.WriteLine()可以看到打印输出。
3.2 手动添加回调处理
- 将按钮命名,e.g:testButton,即为Form1.Designer.cs中的字段名
- 在Form中添加一个回调方法:void Ontest(object sender, EventArgs e)
- 添加事件处理:testButton.Click += new EventHandler(this.Ontest);
public Form1()
{
InitializeComponent();
testButton.Click += new EventHandler(this.Ontest);//事件语法,testButton.Click事件一般写法,事件委托类型EventHandler,传入Ontest
}
public void Ontest(object sender, EventArgs e)//手动添加,要求参数类型不能改,只能修改方法名
{
{
MessageBox.Show("大家好");
}
(四)时间显示小练习
在设计器中添加两个控件——Button和textBox
将Button的属性Text改为"显示时间"——Text为显示文字
将TextBox的属性Name改为"timeField"——Name表示的是字段
Form1.cs:
private void OnButtonClick(object sender, EventArgs e)//点击事件,回调处理
{
string timeStr= DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");//DataTime获取系统时间,并通过定义变量存放当前时间
this.timeField.Text = timeStr;//将时间显示到文本框内,this可省略
}
//注释:每次点击按钮都会刷新文本框内的时间
四、控件的布局
(一)布局的方式:
1. 可视化布局:在设计器里拖放操作
2. 手工布局:用代码计算每个控件的位置
3. 使用布局器:用布局器自动布局
1.1 可视化布局
要点与细节:
当窗口改变大小时,布局不能自动适应,所以此种布局只适用于窗口大小固定不变的情况。
1.2 手工布局——用代码计算每个控件的位置
重写OnLayout方法
override void OnLayout(LayoutEventArgs levent)
{
}
当窗口大小改变时,会自动调用这个方法重新布局
public Form1()
{
InitializeComponent();
this.textBox1.AutoSize = false;// 关闭textbox的默认高度
}
/*手动布局——重写OnLayout方法*/
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);//调用父类的OnLayout(),不是必需的
/*获取窗口大小ClientSize(仅客户区,不包括标题栏)*/
int w = this.ClientSize.Width;//窗体宽度
int h = this.ClientSize.Height;//窗体高度
/*计算每一个控件的位置和大小*/
int yoff = 0;//初始化,yoff表示控件顶部距离
yoff = 4;
/*使用Location和Size属性,计算textBox、button和pictureBox的位置大小*/
this.textBox1.Location = new Point(0,yoff);
this.textBox1.Size = new Size(w-80,30);
this.button1.Location = new Point(w-80,yoff);
this.button1.Size = new Size(80,30);
yoff += 30;//定位第一行的高度,即pictureBox上方控件的底部位置/pictureBox的顶部位置
yoff += 4;//两个控件之间的间隔
this.pictureBox1.Location = new Point(0, yoff);
this.pictureBox1.Size = new Size(w,h-yoff-4);
}
要点与细节:
- 在定义textbox的大小位置是需要将它的AutoSize改为false
- 区分窗口的Size和Clientsize:Size是整个窗体的宽度和高度;Clientsize是客户区的宽度和高度,去掉border和标题栏的宽度
- 窗口的自适应原理:当窗口大小发生变化时,系统框架会自动调用OnLayout()方法
(二)两个通用的布局属性——Anchor和Dock
Anchor:锚定,将控件固定于某个位置
Dock:停靠,将控件停靠在一侧或中央
2.1 Anchor——锚定
同时需要将控件放置到合适的位置;在需要居中时,需要将控件使用【格式】下的【窗体内居中】-【水平/垂直对齐】
- 锚定于左下角:Anchor=Left|Bottom
- 锚定于右下角:Anchor=Right|Bottom
- 锚定于上边缘、水平拉伸Anchor=Top|Left|Right
- 锚定于上边缘、水平居中:Anchor=Top
- 全面拉伸(一般用于占满整个屏幕的情况):Anchor=Top|Right|Bottom|Left
- 居中:先进行水平居中,垂直居中;Anchor:None
......
2.2 Dock——停靠
Top上/Bottom下/Left左/Right右/Fill中/None无
将窗口拉伸后,控件停靠在窗口的相对位置不变
2.3 嵌套布局
在容器内添加子控件/容器,可以利用Anchor和Dock设置子控件/容器相对父控件/容器停靠、锚定
要点与细节
不可以同时设置Anchor与Dock;当设置Dock属性时,Anchor属性无效(界面编辑器自动调整一致)
(三) 布局器
布局器LayoutEngine:负责子控件的布局
默认地,一个Form或Panel都自带了一个布局器;在窗口改变大小时,由窗口的布局器来调整布局
在一个窗口中添加一个控件,设置其Anchor/Dock属性;在运行时Anchor/Dock产生作用,其原理是窗口的LayoutEngine对整个布局进行调整
3.1 自定义布局器
例1 SimpleLayoutPanel:自定义一个Panel,并自己实现一个LayoutEngine
SimpleLayout.cs:(从上到下居中对齐,依次布局排列)
using System.Windows.Forms.Layout;
namespace Form6_1
{
//自定义布局面板
class SimpleLayoutPanel : Panel//自定义布局面板SimpleLayoutPanel继承Panel
{
//布局器
private LayoutEngine layoutEngine;//创建对象
public override LayoutEngine LayoutEngine//重写Panel的LayoutEngine属性
{
//
get
{
layoutEngine = new SimpleLayoutEngine();//调用构造函数SimpleLayoutEngine
return layoutEngine;
}
}
}
//自定义布局器
class SimpleLayoutEngine:LayoutEngine//自定义布局器SimpleLayoutEngine继承LayoutEngine
{
public override bool Layout(object container, LayoutEventArgs layoutEventArgs)//重写布局
{
//容器
SimpleLayoutPanel parent = (SimpleLayoutPanel)container;//定义父类
int w = parent.Width;
int h = parent.Height;
//去除内边距Padding;
int x = parent.Padding.Left;//x表示父面板左边位置
int y = parent.Padding.Top;//y表示父面板上边位置
w -= (parent.Padding.Left + parent.Padding.Right);
h -= (parent.Padding.Top+parent.Padding.Bottom);
int gap = 2;
foreach(Control c in parent.Controls)
{
c.Location = new System.Drawing.Point(x,y);
c.Size = new System.Drawing.Size(w,c.PreferredSize.Height);
y += c.Size.Height;
y += gap;
}
return false;
}
}
}
例2 https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.forms.layout.layoutengine?view=netframework-4.5.1
自定义布局器的使用步骤:
- 【工具|选项】-【Windows窗体设计器|常规】-【自动填充工具箱】:设为True
- 添加自定义Panel或Control的类将写好的自定义布局器.cs文件粘贴至【解决方案管理器】中
- 【生成】-【生成解决方案F7】
- 重新打开Form1.cs,在工具箱界面可以看到自己的控件
3.2 FlowLayoutPanel
FlowLayoutPanel,流式布局。子控件依次排列,一行排满之后换行继续排。
设置FlowlayoutPanel的dock属性停靠在所需位置;在调试之后会发现,将窗口拉小会使FlowLayoutPanel内的子控件自动换行,这就是所谓的流式布局。
控件的选择,在多个控件嵌套叠加时,可以通过右键选择目标控件
要点与细节:
FlowLayoutEngine本身不是一个布局器,它只是一个面板,是它内部实现了一个布局器。
3.2 TableLayoutPanel
TableLayoutPanel,表格布局。以表格形式进行布局。
增加、删除行或列等:①右键直接实现②属性栏中对Rows/Columns编辑
行列属性——大小类型:①绝对值(固定大小)②百分比(占据剩余空间的百分比)③自动大小(根据所需的空间自动分配)
TableLayoutPanel子控件的Dock属性的使用规则由该面板的布局器所决定。