第一部分:WinForm入门基础
一、Winform入门
WinForm 是 Windows Form 的简称,是基于 .NET Framework 平台的客户端(PC软件)开发技术,一般使用 C# 编程。在VS2019中,C# WinForm 编程需要创建「Windows窗体应用程序」项目。Windows 窗体应用程序是 C# 语言中的一个重要应用,也是 C# 语言最常见的应用。使用 C# 语言编写的 Windows 应用程序与 Windows 操作系统的界面类似,每个界面都是由窗体构成的,并且能通过鼠标单击、键盘输入等操作完成相应的功能。WinForm支持可视化设计,简单易上手,并可以接入大量的第三方UI库或自定义控件,给桌面应用开发带来了无限可能。
1、WinForm项目结构
(1)引用:包括所有的系统库文件的引用依赖
(2)App.config:当前项目的配置文件
(3)Form1.cs:当前窗体的事件逻辑源码
– Form1.Designer.cs:当前窗体的控件布局源码
– Form1.resx:当前窗体的资源文件(图片、图标、资源等)
– 注意:
a.Form1.cs和Form1.Designer.cs都定义了Form1类,该类使用了Partial关键词声明,其定义的类可以在多个地方被定义,最后编译的时候会被当作一个类来处理。因此两个文件各司其职,最后合并为一个类编译。
b.要手动实现自定义窗体,可以添加自己的类,然后继承Form类即可
(4)Program.cs:当前项目程序的主入口Main,启动项目,运行初始窗口
namespace WindowsFormsApp_learning
{
//Program.cs 入口程序解读
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
//1.[STAThread] Attributes语法,修饰Main方法。示应用程序的默认线程模型是单线程单元 (STA)
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//2.开启窗口的消息循环,初始化并启动Form1窗口
Application.Run(new Form1());
}
}
}
2、窗口设计与控件布局
(1) 操作:在设计界面拖拽控件,可以完成界面布局(控件大小、名称、类型、样式等)
(2)原理:设计界面自动关联Form1.Designer.cs文件,在InitializeComponent()方法中会自动生成相关代码
(3)设计原则:
– Form1.Designer.cs文件:窗体控件布局文件,一般【不需要我们修改】,只要通过设计界面代码就会自动生成。
– Form1.cs文件:窗体事件逻辑代码的实现,一般【需要我们手动书写】,包括触发事件、回调、数据交互、跳转等等
(4)手动添加控件:不通过设计界面,有两种方式
(1)在Form1.Designer.cs中添加:
private System.Windows.Forms.Button btn_design;//声明控件
//默认的控件初始化方法
InitializeComponent():{
this.btn_design = new System.Windows.Forms.Button();//定义控件
this.btn_design.Text = “自定义控件”;//设置Text属性
this.btn_design.Location = new Point(40,40);//设置布局位置 Point(x,y)
this.btn_design.Size = new Size(100,40);//设置尺寸大小 Size(width,height)
this.Controls.Add(this.btn_design);//注册控件到窗体
}
(2)在Form1.cs中添加:
private Button btn_design;//声明控件
public Form1(){
//先调用Designer.cs中的控件初始化方法
InitializeComponent();
this.btn_design = new System.Windows.Forms.Button();//定义控件
this.btn_design.Text = “自定义控件”;//设置Text属性
this.btn_design.Location = new Point(40,40);//设置布局位置 Point(x,y)
this.btn_design.Size = new Size(100,40);//设置尺寸大小 Size(width,height)
this.Controls.Add(this.btn_design);//注册控件到窗体
}
(3)注意:
1. 一般我们都使用拖拽添加控件,当然也有特殊情况需要我们手动添加(比如自定义控件)
2. 窗体GUI中,左上角为原点(0,0),竖直向下为y轴,水平向右为x轴(宽度表示x轴上长度,高度表示y轴上长度),单位为像素。
3、窗口事件
1、WinForm 自动添加事件处理
(1)操作:在设计界面-控件属性-闪电符号(事件)-添加事件,就会在 Form1.cs中自动生成该控件相应方法名称的事件触发函数
(2) MessageBox.Show():显示弹出消息提示框
(3) GUI界面下Console.WriteLine不显示,需要使用调试模式
namespace WindowsFormsApp_learning
{
/**
* 4.WinForm事件处理
* (1)操作:在设计界面-控件属性-闪电符号(事件)-添加事件,就会自动生成相应方法名称的事件触发函数
* (2) MessageBox:显示消息提示框
* (3)GUI界面下Console.WriteLine不显示,需要使用调试模式
*/
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void showMessage(object sender, EventArgs e)
{
MessageBox.Show(“Hello World!”);
}
}
}
(4).手动添加事件处理(以Btn_design为例)
1)、步骤:
添加按钮控件到布局
书写事件处理函数,必须符合 void function_name(object param1,EventArgs e){}的形式
添加注册事件,this.Btn_design.Click = new EventHandler(this.showTip);
2)、注意:事件处理机制用到了委托和事件的C#基础,有时间可以看一下
下一步的学习请前往 WindowsFormApp_demo1(实现一个显示本地时间的APP)
namespace WindowsFormsApp_learning
{
/**
* 5.手动添加事件处理(以Btn_design为例)
* (1)步骤:
* – 添加按钮控件到布局
* – 书写事件处理函数,必须符合 void function_name(object param1,EventArgs e){}的形式
* – 添加注册事件,this.Btn_design.Click = new EventHandler(this.showTip);
* (2)注意:事件处理机制用到了委托和事件的C#基础,有时间可以看一下
* 下一步的学习请前往 WindowsFormApp_demo1(实现一个显示时间的APP)
*/
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//2.1 注册Click事件为手动添加的函数
this.Btn_design.Click = new EventHandler(this.showTip);
}
public void showTip(Object sender,EventArgs e)
{
MessageBox.Show(“手动添加!”);
}
}
}
4、时间显示器小练习
设计一个能获取本地时间并显示在文本框的时间查看器,设计思路就是: 1、在UI设计界面添加一个按钮Button和一个用来显示时间的文本框TextBox
(1)界面设计
(2)逻辑代码
namespace WindowsFormsApp_Demo1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void ShowTime(object sender, EventArgs e)
{
string time = DateTime.Now.ToString(“yyyy-MM-dd HH:mm:ss”);
this.Time_filed.Text = time;
}
}
}
二、WinForm布局开发
1、常见的WinForm 自动界面布局(设计界面拖拽布局)
(1)操作:拖拽控件,放置在UI设计界面上布局
(2)技巧:通过ctrl 多个控件选定,可以在上方工具栏进行细节的对齐微调,比如居中对齐、左对齐等。
(3)本质:每次拖拽添加一个布局控件,就会在窗口相应的Designer.cs设计布局代码文件中的InitializeComponent()方法中自动生成布局代码(比如Location、Size等),来实现布局。
(4)存在的问题:自动布局虽然方便灵活,但是当窗口大小拉伸改变时,布局控件不能实现自动适应,仍会保持原大小,因此自动布局只适用于窗口大小不变的情况
2、手动布局解决自适应问题
1)、WinForm界面手动布局(并解决自适应问题)
(1)步骤:
– 通过设计界面拖拽/手动注册组件的方式,初始化初始界面布局(此时属于自动布局)
– 在Form.cs逻辑代码中重写Form父类的OnLayout方法,在方法内实现手动布局。OnLayout方法会在窗口大小变化时自动被回调调用,来重新设置组件的位置大小等属性实现自适应。
a.调用父类的OnLayout(),不是必须的。
b.获取当前窗口大小 CilentSize(仅客户区,不含标题栏)
c.计算和设置每一个控件新的的大小和位置,实现动态布局
(2)注意:
– Size属性指窗口大小(包括工具栏),ClientSize指客户区大小(不包含工具栏)
– 本质:OnLayout方法会在窗口大小变化时自动被调用,来设置组件的位置实现自适应
namespace WindowsFormsApp_learning
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//重写父类的OnLayout方法,实现手动布局自适应
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.text_box.Location = new Point(0, yoff);//坐标(0,4)
this.text_box.Size = new Size(w – 80, 30);//尺寸(w-80,30)
this.btn_click.Location = new Point(w – 80, yoff);//坐标(w-80,4)
this.btn_click.Size = new Size(80, 30);//尺寸(80,30)
yoff = 30;//第一行的高度
yoff = 4;//间隔
this.panel1.Location = new Point(0, yoff);
this.panel1.Size = new Size(w, h – yoff – 4);
}
}
}
2)、WinForm布局属性
WinForm布局属性(控件与布局相关的属性,设计界面可见)
(1)Anchor:固定、锚定(无论窗口怎么变化,控件的相对位置都不变)
– 含义:固定控件与父窗口的【上下左右边距】不变,实现控件的锚定
– 左上角固定:选定 Top,Left(默认)
– 顶部固定,水平拉伸:选定 Top,Left,Right
– 居中固定:None(取消所有)
– *注意:Anchor效果的好坏取决于控件的初始位置,因为控件的初始位置决定了控件的边距!所以要让控件固定右上角,就要初始设计时就放在右上角!
(2)Dock:停靠属性,将控件停靠在父窗口的一侧或者中央
– Top:上,控件停靠在容器上侧,宽度填满容器,高度可以调节
– Bottom:下,控件停靠在容器下侧,宽度填满容器,高度可以调节
– Left:左,控件停靠在容器左侧,高度填满容器,宽度可以调节
– Right:右,控件停靠在容器右侧,高度填满容器,宽度可以调节
– Fill,中,控件填满容器中间剩余部分(所以说与控件添加顺序有关)
– None,无,不使用Dock属性
– *注意:
a.容器之间可以嵌套,使用布局属性(容器就是存放组件的,比如Pane)
b.Dock属性与Anchor属性不能同时使用
c.Dock属性不是固定边距了,就是相对位置填充
3、WinForm布局器
1)、布局器 LayoutEngine
(1)含义:布局器就是容器内部组件的默认排列方式,所有的容器都带有一个默认的布局器
(2)自定义控件如何加入设计界面:
– 工具 -> 选项 -> Windows窗体设计器 -> 常规 -> 自动填充工具箱 -> True
– 添加自定义的控件类源码到项目下
– 生成/重新生成结局方案 F7
– 重新打开设计界面,在工具箱就可以看到自己的控件
(3)系统提供的布局器-FlowLayoutPanel(流式布局):
– 含义:普通的Panel面板容器控件,使用了FlowLayout流式布局,即所有内部组件按照顺序从左到右从上到下依次排列
– FlowLayoutPanel也可以使用Anchor、Dock等属性,因为他也是窗体容器内的一个组件
– 控件的选择技巧:多个控件叠加到一起时,可以右键最上层控件,来进行重叠控件的选择
(4)系统提供的布局器-TableLayoutPanel(表格布局):
– 含义:普通的Panel面板容器,使用了TableLayout表格布局,即所有内部组件可以放置到不同的表格中(每个表格只能放置一个控件)
– TableLayoutPanel也可以使用Anchor、Dock等属性,因为他也是窗体容器内的一个组件。搭配布局与布局属性,可以实现很多不同的布局效果。
– 删除/添加行、列:右键表格Panel,可以添加、删除行/列
– 调整表格大小:表格属性Columns可以通过绝对值、百分比、自动调整等方式来调整单元格的大小
a.绝对:固定像素值大小
b.百分比:除去绝对大小后,单元格占剩余大小的百分比
c.自动调整:根据内部控件的大小生成
– 跨行/列控件:在控件的ColumnSpan/RowSpan调整控件可以跨行/跨列布局
– *注意:TableLayoutPanel 控件每个单元格只能包含一个子控件。
(5)系统提供的布局工具-默认布局:默认布局可以通过控件拖拽放置,并可以使用Anchor、Dock来调整
(6)自定义布局器:用户可以自己设计实现布局器,这属于WinForm高级的内容,后面学习。
三、WinForm常用控件
1.Winform 组件的使用
(1)TextBox:输入文本框
– 常用属性:尺寸Size、单行/多行Multiline、密码输入 PasswordChar、只读ReadOnly、显示/获取文本Text等
(2)CheckBox:复选框
– 常用属性:尺寸Size、显示文本Text、Checked是否勾选
(3)ComboBox:下拉列表(只能单选)
– 常用属性:
b.其他设置:Size尺寸、Text提示文本等
– 常用事件:
a.获取选中的项:SelectedItem(选中项的值)、SelectedIndex (选中项的索引,-1表示未选中)
b.选项改变事件:SelectedIndexChanged
– 注意:Item可以添加字符串,也可以添加任意object类型(比如自定 义对象),对象会以object.toString显示
(4)ListBox:列表框(展示数据、可单选/多选)
– 常用属性:SelectionMode(单选/多选模式)
a.单选模式获取选项:SelectedItem/SelectedIndex(选中项的 值/索引)
b.多选模式获取选项:SelectedIndices/SelectedItems(选中项 的索引集合/值集合)
– 常用事件:SelectedIndexChanged(选项改变)
– 注意:Item可以添加字符串,也可以添加任意object类型(比如自定 义对象),对象会以object.toString显示
界面展示
2.实体类 Student(封装信息)
1.C# 类的属性与字段
(1)字段:字段是类的成员变量,用于类内特征的数据存储(静态) 比如 private int Id;
(2)属性:属性是对字段特征的动态描述,用于向外界提供数据,本质上就 是get、set方法。
– 完整属性用法:
private string sdu_name;//声明字段
public string StudentName{//声明属性
get{ return sdu_name; }
set{
if(value==null)sdu_name = “admin”;
sdu_name = value;
}
}
– get访问器:get访问器必须包含return语句,返回字段‘realValue’。用于获取数据,如果没有get则字段为只写的
– set访问器:set访问器接受它的输入参数–value,并把它赋给字段,value是隐式参数。用于设置数据,如果没有set则字段为只读的
(3)使用方式:直接使用Class.StudentName来获取和设置数据,就像是直接使用公开的属性一样
(4)自动属性:简化字段和属性的关系
– 用法:public 数据类型 属性名{ get; set; }
– *原理:c# 允许只声明属性而不声明后备字段,编译器会创建隐藏的
后备字段。并且自动挂接到get,set访问器上。
(5)注意:
– 属性也可以不与字段关联,可以当作函数进行复杂计算,比较自由灵活
– 属性也可不用显示声明相应的字段,直接用于存贮数据。(自动属性)
– 也可以通定义其他set、get函数来实现访问private字段的功能
namespace WindowsFormsApp_learning
{
class Student
{
//属性访问器
public int SduId { get; set; }
public string SduName { get; set; }
public bool SduSex { get; set; }
public string SduPhone
{
get; set;
}
public Student(int sduId, string sduName, bool sduSex, string sduPhone)
{
SduId = sduId;
SduName = sduName;
SduSex = sduSex;
SduPhone = sduPhone;
}
public Student()
{
}
}
}
3.逻辑事件代码Form.cs
namespace WindowsFormsApp_learning
{
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
//初始化数据
LoadData();
}
/**
* 保存按钮触发事件-保存学生信息到本地文件
*/
private void save_student(object sender, EventArgs e)
{
Student student = new Student();
student.SduId =Convert.ToInt32(sdu_Id.Text.Trim()); //Convert格式转换
student.SduName = sdu_Name.Text.Trim();
student.SduSex = (sdu_sex.SelectedIndex == 1);
student.SduPhone = sdu_phone.Text.Trim();
//以Json格式保存到本地文件
string JsonStr = JsonConvert.SerializeObject(student, Formatting.Indented);
AfTextFile.Write(“student.txt”, JsonStr,AfTextFile.UTF8);
MessageBox.Show(“操作成功”);
}
//读取文件,加载数据
private void LoadData()
{
string JsonStr = AfTextFile.Read(“student.txt”, AfTextFile.UTF8);
if (JsonStr == null) return;
Student student = JsonConvert.DeserializeObject<Student>(JsonStr);
sdu_Id.Text = Convert.ToString(student.SduId);
sdu_Name.Text = student.SduName;
sdu_sex.SelectedIndex = student.SduSex?1:0;
sdu_phone.Text = student.SduPhone;
}
}
}
四、图片框与项目资源
1、图片框与资源
– 本地文件 :本地资源路径,比如”D\\Resources\\123.jpg”(注意使 用Windows路径格式)
– 项目资源文件:Properties/Resources.resx(项目资源文件夹下)
a.如何添加项目资源文件:双击Properties/Resources.resx->添加资源->添加现有文件->选择本地图片->重命名(规范化,以后使用时直接调用名字使用资源)
b.能添加什么类型的资源:字符串、文本文档、图片、视频、音频等
c.原理:Resources.resx将添加的资源整合到了项目exe当中,因此就算删除源文件也不影响项目运行。在调用资源时,是通过自动生成的方法来获取资源。
d.优点:将资源统一到项目中来,并且可以通过规范化的命名直接调用资源。
e.使用:通过 Properties.Resources.资源名称; 来直接调用你的资源,方便快捷
– 网络资源:比如”http://baidu/image/meinv.jpg”
(2)图片资源加载类:
– C# 提供了Image抽象类,作为所有图片资源的总称
– C# 提供了Image抽象类的具体实现类 Bitmap(位图、像素图),来具体加载读取图像。
a.比如Bitmap img = new Bitmap(“D\\Resources\\123.jpg”);
b.比如Bitmap img = Properties.Resources.img_Conv;
(3)图片框控件:PictureBox
– Size、Location属性:尺寸、位置
– Image属性:图片资源(可以通过本地/项目资源文件导入)
– SizeMode属性:图片的适应模式(StretchImage拉伸图片和图片框 相同尺寸、Zoom缩放等)
– ErrorImage、InitialImage属性:加载失败、加载图片显示之前所显 示的图片
(4)图片框控件可视化配置:在设计里拖动PictureBox并配置属性即可。
(5)图片框代码手动配置:
Bitmap img = Properties.Resources.img_Conv; //读取图片资源
pictureBox.SizeMode = PictureBoxSizeMode.Zoom;//手动设置图片格式
2、添加资源
3、图片框控件展示
namespace WindowsFormsApp_learning
{
public partial class Form2 : Form
{
public Form2()
{
//初始化所有组件
InitializeComponent();
//初始化PictureBox控件-图片资源
Bitmap img = Properties.Resources.img_Conv;
pictureBox.Image = img;
pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
}
}
}
第二部分、WinForm进阶与复杂控件使用
一、复合控件
1.控件的使用综述:在WinForm中使用控件主要包括以下几种情况
(1)标准控件:直接使用winform自带的控件,比如button、textBox等。我们只需要修改属性,添加事件即可。
(2)复合控件:将一些标准控件组合起来,实现复杂功能。需要定义 class YourControl:UserControl{ }
(3)扩展控件:拓展或者改变一些标准控件的功能。需要继承于标准控件,class YourButton:Button { }
(4)自定义控件:完全自定义一个控件,定义属性、方法、样式等。需要继承于Control(Control是所有控件的基类),class YourControl:Control { }
*注意:我们之前学习了标准控件的使用,所以这节课我们就学习一下复合控件如何定义和使用,循序渐进。
2.复合控件:实现一个搜索框控件(组合文本框与图片框)
(1)定义:复合控件又称为用户控件,专门用来将标准控件进行组合,其继承自 UserControl
(2)定义复合控件:
– 右键解决方案->添加 用户控件窗体(UserControl),命名为 SearchBox。会自动生成一个SearchBox类,继承自 UserControl
– 双击 SearchBox.cs文件,可以打开设计界面,像窗口一样设计符合组 件的样式
a. 拖上去一个textBox文本框作为搜索框
b. 拖上去一个图片框,设置图片为搜索图标
c. 使用表格布局,将二者组合到一起,并调整大小和位置
(Anchor、Dock、表格比例等)
(3)复合控件的摆放和原理
– 在界面中可以直接拖拽符合控件,并且复合控件作为一个整体也具有 Size、Location、事件等等常规属性。
– 复合控件本质上就是一个窗体!复合控件中的组件和窗体中的组件一 样,在初始化时放置到窗体上。
1、复合控件SearchBox可视化设计
在SearchBox.cs窗体设计界面中,对复合控件进行可视化样式设计,其步骤如下:
添加一个TableLayoutPanel表格布局容器,保留一行两列。并设置其Dock为Fill填满整个父窗口,然后设置两列表格大小比例为7:3。(用来放置文本框和图片框按钮)
添加一个TextBox到第一个单元格,并设置其Anchor为左右边距固定(可以随父窗口拉伸而拉伸)
添加一个pictureBox到第二个单元格,并设置其Dock为Fill填满单元格,添加图片资源-搜索图标
2、复合控件的属性访问和事件处理
1). 复合控件的访问和事件处理:我们有时需要获取复合控件内子控件的数据、或者给复合控件的某个位置(图片、按钮)设置事件
(1)设置public直接访问(最简单,最直接,最不常用):将复合控件内的子控件成员全部设置为public,直接访问即可
– 在复合控件设计界面内选择每个子控件,设置其 Modifiers 属性为 public (默认为private)
– 然后在复合控件源码中,相关的子控件就会自动变为 public 访问修饰 的,我们可以直接使用 [复合控件.子控件.属性] 来访问
– 获取文本框搜索内容:MessageBox.Show(“开始搜索…” searchBox1.textBox1.Text);
(2)在复合控件内设置事件和获取数据,就可以像窗口一样直接使用子组件了(没试过)
(3)复合控件自定义属性
– 添加属性:
a.在SearchBox逻辑代码中,添加属性访问器
public string SearchText
{
get
{
return this.textBox1.Text;
}
set
{
this.textBox1.Text = value;
}
}
b.重新生成解决方案,打开Form3界面设计,在杂项中就可以看到我们自定义的属性SearchText。通过修改值可以直接修改内部的TextBox。其本质都是调用了set、get方法。
– 重写属性:
a.在SearchBox逻辑代码中,重写UserControl已有的属性,比如Text等
public override string Text {
get
{
return this.textBox1.Text;
}
set
{
this.textBox1.Text = value;
}
}
b.重新生成解决方案,打开Form3界面设计,原本的属性就成为了我们重写的属性。
– C# Attribute 特性:特性是一种可以被编译器识别的声明性标签,类似于Java的注解
a.语法:[attribute(positional_parameters, name_parameter = value, …)]
b.常用特性:
[Browsable(true)]: 控件属性是否在设计器内可见(一般默认为false)
[Category(“Appearance”)]:控件属性显示在哪个类别中。Action(与可用操作相关)、Appearance(与实体的外观相关)、Behavior(与实体的行为相关)、Data(与数据和数据源管理相关)
[DesignerSerializationVisibility()]: 将我们在控件属性上设定的值持久化到代码中,这样我们下次再查看控件的值依然是我们最后一次设定的值。指示一个属性是否串行化和如何串行化,它的值是一个枚举,一共有三种类型Content,Hidden,Visible。
(4)复合控件自定义事件
– 在SearchBox逻辑代码中,添加声明自定义事件。public event EventHandler SearchEvent;
– 重新生成解决方案后,可以在事件-杂项中看到我们自定义的事件 SearchEvent
– 在设计界面可以给事件添加/实现处理回调方法 onSearch(),配合自定义属性食用
private void onClick(object sender, EventArgs e)
{
//调用事件,固定写法
if(SearchEvent != null)
{
SearchEvent.Invoke(this, e);
}
//简化写法 SearchEvent?.Invoke(this,e);
}
*注意:一个控件还可以添加多个事件,顺序触发
(1)界面设计
(2)SearchBox.cs 逻辑代码
namespace WindowsFormsApp_learning
{
public partial class SearchBox : UserControl
{
//添加自定义事件
public event EventHandler SearchEvent;
public SearchBox()
{
//初始化控件(TextBox PictureBox)
InitializeComponent();
}
/**
* 添加新的自定义属性 SearchText
*/
public string SearchText
{
get
{
return this.textBox1.Text;
}
set
{
this.textBox1.Text = value;
}
}
/**
* 重写属性 Text
*/
[Browsable(true)]
public override string Text {
get
{
return this.textBox1.Text;
}
set
{
this.textBox1.Text = value;
}
}
//注意:SearchEvent的事件处理方法,需要在Form.cs中像基本控件的事件处理一样进行添加才行
private void onClick(object sender, EventArgs e)
{
//调用事件,固定写法
if(SearchEvent != null)
{
SearchEvent.Invoke(this, e);
}
//简化写法 SearchEvent?.Invoke(this,e);
}
}
}
(3)Form3.cs 主窗体逻辑代码
namespace WindowsFormsApp_learning
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//searchBox1.pictureBox1.Click = new EventHandler(searchEvent);
}
private void searchEvent(object sender,EventArgs args)
{
//获取并展示 文本框的内容
MessageBox.Show(“开始搜索…” searchBox1.textBox1.Text);
}
//SearchBox的自定义事件实现 SearchEvent -> onSearch
private void onSearch(object sender, EventArgs e)
{
//获取并展示 文本框的内容(通过自定义属性/重写属性)
MessageBox.Show(“开始搜索…” searchBox1.SearchText);
}
}
}
二、控件包装
1. 控件包装基本设计
1)控件包装:
(1)定义:控件包装是复合控件的一种特殊形式,其内部只有一个控件
(2)目的:对一些基础控件的功能进行拓展和自定义修改,比如TextBox包装后可以修改高度和内边距padding等
(3)步骤:
– 新建包装控件类AfTextBox,继承自UserControl,其内部只放置一个TextBox组件,作为对TextBox的包装拓展
– 为了实现可修改TextBox高度宽度的效果,需要设置TextBox的Size随着外部Form的变化而变化 -> onLayout()
– 为了实现可修改TextBox内边距padding的效果,需要设置Form与TextBox的边距来代替文字内边距 -> onLayout()
– 为了无缝实现上述效果,看着是一个TextBox整体,需要设置TextBox和Form背景色相同,并且取消TextBox边框。
2). Location属性解释:控件左上角相对于其容器左上角的坐标,单位是像素。
(1)对于窗体来说,就是窗口左上角相对于屏幕左上角的坐标;
(2)对于一般控件来说,就是控件左上角相对于其容器左上角的坐标,比如说相对于窗体,图片框等。
(3)父容器左上角默认(0,0)
3). 窗口位置调节:
– this.StartPosition = FormStartPosition.Manual; //窗体的位置由Location属性决定
– this.StartPosition = FormStartPosition.CenterParent; //窗体在其父窗体中居中
– this.StartPosition = FormStartPosition.CenterScreen; //窗体在当前显示窗口中居中,尺寸在窗体大小中指定
– this.StartPosition = FormStartPosition.WindowsDefaultBounds; //窗体定位在windows默认位置,边界也由windows默认决定(默认)
– this.StartPosition = FormStartPosition.WindowsDefaultLocation; //窗体定位在windows默认位置,尺寸在窗体大小中指定
(1)TextBox包装控件AfTextBox 逻辑代码
namespace WindowsFormsApp_learning
{
public partial class AfTextBox : UserControl
{
public AfTextBox()
{
//初始化内部控件
InitializeComponent();
}
//重写布局实现
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
//1.获取子控件
if (this.Controls.Count == 0) return;
Control C = this.Controls[0];
//2.获取父窗口参数
Padding p = this.Padding;//父窗口的边距Padding
int x = 0, y = 0;//子控件相对于容器的初始坐标(左上角)
int w = this.Width, h = this.Height;
w -= (p.Left p.Right);//TextBox的宽度 = 容器宽度 – 左右边距
x = p.Left;//TextBox的相对x = p.Left
//3.计算文本框的高度,使其显示在中间
// (1)PreferredSize:获取可以容纳控件的矩形区域的大小。
// (2)Size:获取控件的大小
int h2 = C.PreferredSize.Height;
if (h2 > h) h2 = h;
y = (h – h2) / 2;//TextBox的相对y = (h-h2)/2 高度居中
//4.设置子控件的位置和尺寸
C.Location = new Point(x, y);
C.Size = new Size(w, h2);
//5.为什么不调整高度大小:文本框为单行,高度调节无效。只会随着文字格式的大小变化!
}
}
}
2.控件包装单文件开发
1).包装、复合控件单文件开发
(1)定义:每次创建UserControl都会自动给我们创建一个相应的designer.cs布局设计文件,将界面和逻辑分开开发。如何将二者合并到一个文件开发呢?
(2)步骤:
– 【手动】创建AfTextBox2普通类 class文件,并手动继承UserControl
– 双击打开AfTextBox2.cs可以进入设计界面,拖放控件。会直接给AfTextBox2.cs内自动添加控件布局(不会分开添加了)
– 手动实现构造函数,初始化调用布局 InitializeComponent()
– 添加逻辑代码和事件、属性
(3)好处:以后可以直接将单个控件cs文件复制到别的项目里直接使用,而不用分多个了
2).包装控件的自定义属性和事件:和复合控件一样(AfTextBox2为例)
(1)添加/重写自定义属性 Text、BackColor、Front
(2)添加自定义事件 ReturnPressed(回车事件)
(1)AfTextBox2包装控件cs逻辑代码
namespace WindowsFormsApp_learning
{
class AfTextBox2 : UserControl
{
/**
* 自动生成布局代码,直接添加到本文件
*/
private TextBox edit;
private void InitializeComponent()
{
this.edit = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// edit
//
this.edit.BorderStyle = System.Windows.Forms.BorderStyle.None;
//this.edit.Font = new System.Drawing.Font(“宋体”, 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.edit.Location = new System.Drawing.Point(106, 79);
this.edit.Name = “edit”;
this.edit.Size = new System.Drawing.Size(263, 37);
this.edit.TabIndex = 0;
this.edit.KeyPress = new System.Windows.Forms.KeyPressEventHandler(this.OnPress);
//
// AfTextBox2
//
this.BackColor = System.Drawing.Color.White;
this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.Controls.Add(this.edit);
this.Name = “AfTextBox2”;
this.Size = new System.Drawing.Size(524, 246);
this.ResumeLayout(false);
this.PerformLayout();
}
/**
* 手动添加构造函数,初始化布局
*/
public AfTextBox2()
{
InitializeComponent();
}
/**
* 添加逻辑代码,实现组件自适应
*/
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
//1.获取子控件
if (this.Controls.Count == 0) return;
Control C = this.Controls[0];
//2.获取父窗口参数
Padding p = this.Padding;//父窗口的边距Padding
int x = 0, y = 0;//子控件相对于容器的初始坐标(左上角)
int w = this.Width, h = this.Height;
w -= (p.Left p.Right);//TextBox的宽度 = 容器宽度 – 左右边距
x = p.Left;//TextBox的相对x = p.Left
//3.计算文本框的高度,使其显示在中间
// (1)PreferredSize:获取可以容纳控件的矩形区域的大小。
// (2)Size:获取控件的大小
int h2 = C.PreferredSize.Height;
if (h2 > h) h2 = h;
y = (h – h2) / 2;//TextBox的相对y = (h-h2)/2 高度居中
//4.设置子控件的位置和尺寸
C.Location = new Point(x, y);
C.Size = new Size(w, h2);
//5.为什么不调整高度大小:文本框为单行,高度调节无效。只会随着文字格式的大小变化!
}
/**
* 重写自定义属性 Text
* Browsable:True则属性在设计界面显示可修改,False则属性在设计界面不显示。默认为True
* DesignerSerializationVisibility:设置属性的序列化方式,默认为Visible
*/
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text {
get
{
return edit.Text;
}
set
{
edit.Text = value;
}
}
//重写自定义属性 Color
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color BackColor
{
get
{
return edit.BackColor;
}
set
{
//容器和TextBox的颜色一起整体变化
base.BackColor = value;
edit.BackColor = value;
}
}
/**
* 注意:Font的设置有些特殊,需要提供初始的默认值
* **此处仍未解决,无法修改Font字体**
*/
private Font initFont = new Font(“宋体”, 10f);
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public override Font Font
{
get
{
return initFont;
}
set
{
this.initFont = value;
this.edit.Font = value;
}
}
/**
* 添加自定义事件
*/
public event EventHandler ReturnPressed;
//TextBox的keyPress -> 触发 ReturnPressed事件
private void OnPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (ch == ‘\r’)//如果是回车符号
{
//回调ReturnPressed事件
ReturnPressed?.Invoke(this, e);
}
}
}
}
(2)Form窗体应用
{
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
//包装控件的 ReturnPressed 回车触发事件
private void OnEnterPress(object sender, EventArgs e)
{
MessageBox.Show(“正在检索…” afTextBox21.Font);
}
}
}
三.对话框
1.对话框基本概念
1).对话框
(1)构建:对话框本质还是一个Form窗体,只不过以对话框的形式显示。所以对话框的构建参照窗体的设计即可
MyDialog dialog = new MyDialog(); //新建窗体
dialog.ShowDialog(); //作为对话框弹出在当前界面。注意如果是Show()则是显示窗体,非对话框
dialog.Dispose(); //销毁窗体,释放资源
(3)运行流程:
– 在调用ShowDialog方法后,会[阻塞]父窗口进程,不再往下执行。表现为子窗口可以活动,而父窗口不行
– 对话框阻塞直到用户关闭对话框,父窗口进程才继续向下执行
– 执行Dispose方法释放对话框资源
(4)对话框属性(和窗体Form属性一样):
– Text:窗体左上角显示文字
– MaximizeBox:最大化按钮(True显示,Flase不显示)
– MinimizeBox:最小化按钮(True显示,Flase不显示)、
– ShowInTaskBar:是否在任务栏显示
– StartPosition:窗体显示位置 WindowsDefaultLocation窗口默认位置、CenterSceen窗口中央、CenterParent父窗口中央等
– FormBorderStyle:边界样式 Sizable可修改、FixedDialog固定对话框大小、FixedSignle固定窗体
(5)对话框返回值与数据传递
– 对话框含有属性 DialogResult
DialogResult.OK :设置DialogResult为OK,窗体会立即关闭,并返回DialogResult.OK值
DialogResult.Cancel:设置DialogResult为Cancel,窗体会立即关闭,并返回DialogResult.Cancel值
– 数据传递:可以将传递数据组件设置为public,在关闭窗口后Dispose之前调用dialog.xxx来获取属性值
(1)对话框界面设计
(2)对话框逻辑代码(MyDialog.cs)
namespace WindowsFormsApp_learning
{
public partial class MyDialog : Form
{
public MyDialog()
{
InitializeComponent();
}
//对话框确认按钮
private void OnClickOk(object sender, EventArgs e)
{
//关闭当前对话框,并返回OK
this.DialogResult = DialogResult.OK;
}
//对话框取消按钮
private void OnClickCancel(object sender, EventArgs e)
{
//关闭当前对话框,并返回Cancel
this.DialogResult = DialogResult.Cancel;
}
}
}
(3)Form窗体逻辑代码
1.两大功能:
获取对话框返回值,并显示在主窗体界面上
namespace WindowsFormsApp_learning
{
public partial class Form5 : Form
{
public Form5()
{
InitializeComponent();
}
private void show_Dialog(object sender, EventArgs e)
{
MyDialog dialog = new MyDialog(); //新建窗体
DialogResult res = dialog.ShowDialog(); //作为对话框弹出在当前界面
//阻塞当前进程,直到用户关闭对话框
{
//获取数据,显示在父界面
this.textPanel.Text = (dialog.dlg_text.Text “\r\n”);
}
dialog.Dispose(); //销毁窗体,释放资源
}
}
}
2.快捷操作 默认行为:
– 对话框按钮快捷关闭:按钮上带有一个 DialogResult属性:OK、Cancel等都可以设置。其效果和手写代码是一样的,关闭对话框并返回值
3.对话框小练习:字体设置器
(1)主窗口界面设计
(2)对话框界面设计
(3)对话框逻辑代码StyleDialog.cs
namespace WindowsFormsApp_Demo2
{
public partial class StyleDialog : Form
{
public StyleDialog()
{
InitializeComponent();
}
//获取选择字体类型 public
public string FontType
{
get
{
return (string)this.dlg_wordType.SelectedItem;
}
set
{
this.dlg_wordType.SelectedItem = value;
}
}
//获取选择字体大小 public
public int FontSize
{
get
{
return (int)this.dlg_wordSize.Value;
}
set
{
this.dlg_wordSize.Value = value;
}
}
private void onClickOk(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
}
private void onClickCancel(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
}
}
(4)主窗体逻辑代码Form1.cs
namespace WindowsFormsApp_Demo2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void onClickStyle(object sender, EventArgs e)
{
StyleDialog dlg = new StyleDialog();
DialogResult res = dlg.ShowDialog();
if(res == DialogResult.OK)
{
//获取返回值,设置当前字体样式
string fontType = dlg.FontType;
int fontSize = dlg.FontSize;
this.text_panel.Font = new Font(fontType, fontSize);
}
dlg.Dispose();
}
}
}
四.系统对话框
1.系统对话框:WinForm提供了很多内置的系统对话框,比如文件浏览、保存,颜色、字体选择等。
(1)OpenFileDialog : 文件打开对话框
– new OpenFileDialog():新建对话框
– InitialDirectory:设置打开的初始展示路径
– DefaultExt:获取或设置默认显示的文件扩展名。
– Filter:获取或设置当前文件名筛选器字符串。其格式为 “自定义字符串标签|类型列表”
a.单个文件类型:图片|*.jpg,*.png,*.gif
b.多个文件类型:图片|*.jpg,*.png,*.gif|文本|*.txt,*.doc|所有|*.*
– FileName:获取或设置对话框中选定的文件名称字符串
– FileNames:获取或设置对话框中所有选定的文件名称字符串列表
(2)SaveFileDialog : 文件保存对话框(跟OpenFileDialog差不多)
(3)FolderBrowserDialog:文件夹选择对话框
– new FolderBrowserDialog():新建对话框
– InitialDirectory: 获取或设置打开的初始路径
– SelectedPath:获取或设置选择的文件夹路径
(4)ColorDialog:颜色选择对话框
(5)FontDialog:字体选择对话框
2.系统对话框功能使用
为了展示系统对话框的功能,此处设计一个文件浏览窗口,能够选择、保存文件或文件夹,并将所选定的文件/目录路径名称展示到主界面文本框。
(1)界面设计
(2)Form.cs逻辑代码
namespace WindowsFormsApp_learning
{
public partial class Form6 : Form
{
public Form6()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog(); //新建文件对话框
fileDialog.InitialDirectory = “D:\\日常材料\\学习资料\\WinForm\\第十二章 系统对话框\\12.1 系统对话框\\项目源码\\FormApp1201\\FormApp1201”; //设置初始路径
//fileDialog.SelectedPath = Path.GetFullPath(“.”);
fileDialog.DefaultExt = “.cs”; //筛选展示 .cs后缀文件
fileDialog.Filter = “代码|*.cs;*.config”; //设置文件选择器
if (fileDialog.ShowDialog() == DialogResult.OK)
{
//获取并设置文件路径名称到主窗体
string fileName = fileDialog.FileName;
this.textBox1.Text = fileName;
}
}
private void button2_Click(object sender, EventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = “新的文本文件”;
dlg.DefaultExt = “.txt”;
dlg.Filter = “文本|*.txt;*.doc”;
if (dlg.ShowDialog() == DialogResult.OK)
{
string fileName = dlg.FileName;
this.textBox1.Text = fileName;
}
}
private void button3_Click(object sender, EventArgs e)
{
FolderBrowserDialog dlg = new FolderBrowserDialog();
dlg.SelectedPath = Path.GetFullPath(“.”);//返回当前文件夹的绝对路径字符串
if (dlg.ShowDialog() == DialogResult.OK)
{
string path = dlg.SelectedPath;
this.textBox1.Text = path;
}
}
}
}
2.系统对话框练习-图片查看器
(1)界面设计
主窗体中用到了一个自定义文本框AfTextBox,可以自由拉伸。同时有一个浏览按钮放置在其右侧,可以打开文件目录浏览窗口。主窗体还包括一个列表框,用于显示图片名称列表;除此之外还有一个图片框,用于展示指定路径图片。
(2) 自定义对话框AfTextBox单文件开发
namespace WindowsFormsApp_Demo
{
class AfTextBox:UserControl
{
private TextBox edit;
private void InitializeComponent()
{
this.edit = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// edit
//
this.edit.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.edit.Font = new System.Drawing.Font(“宋体”, 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.edit.Location = new System.Drawing.Point(52, 52);
this.edit.Name = “edit”;
this.edit.Size = new System.Drawing.Size(89, 19);
this.edit.TabIndex = 0;
this.edit.KeyPress = new System.Windows.Forms.KeyPressEventHandler(this.onTextPressed);
//
// AfTextBox
//
this.BackColor = System.Drawing.Color.White;
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Controls.Add(this.edit);
this.Name = “AfTextBox”;
this.Size = new System.Drawing.Size(199, 105);
this.ResumeLayout(false);
this.PerformLayout();
}
public AfTextBox()
{
InitializeComponent();
}
protected override void OnLayout(LayoutEventArgs e)
{
base.OnLayout(e);
if (this.Controls.Count == 0) return;
Control c = this.Controls[0];
int w = this.Width, h = this.Height;
int x = 0, y = 0;
int left = this.Padding.Left, right = this.Padding.Right;
w -= (left right);
x = left;
//PreferredSize获取控件矩形容器框的大小
//原因是文本框为单行输入,没法调节高度!但是外部矩形容器可以
int h2 = c.PreferredSize.Height;
if (h2 > h) h2 = h;
y = (h – h2) / 2;
c.Location = new Point(x, y);
c.Size = new Size(w, h2);
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text {
set
{
this.edit.Text = value;
}
get
{
return this.edit.Text;
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color BackColor
{
get
{
return this.edit.BackColor;
}
set
{
this.edit.BackColor = value;
//此处是base!!!!不是this!!!
base.BackColor = value;
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Color ForeColor
{
get
{
return edit.ForeColor;
}
set
{
edit.ForeColor = value;
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReadOnly
{
get
{
return this.edit.ReadOnly;
}
set
{
this.edit.ReadOnly = value;
}
}
public event EventHandler ReturnPressed;
private void onTextPressed(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (ch == ‘\r’)
{
ReturnPressed?.Invoke(this, e);
}
}
}
}
(3)主窗体Form.cs逻辑代码
namespace WindowsFormsApp_Demo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void onScanClick(object sender, EventArgs e)
{
FolderBrowserDialog dlg = new FolderBrowserDialog();
if(dlg.ShowDialog() == DialogResult.OK)
{
string path = dlg.SelectedPath;
this.afTextbox.Text = path;
// 加载图片列表
ShowPictureList(path);
}
}
private void ShowPictureList(string dir)
{
// 清空显示
listFile.Items.Clear();
// 遍历所有的文件,检查文件名后缀
string[] files = Directory.GetFiles(dir);
foreach(string f in files)
{
if (f.EndsWith(“.jpg”) || f.EndsWith(“.png”) || f.EndsWith(“.jpeg”))
{
// 取得文件名
PictureListItem item = new PictureListItem();
item.name = Path.GetFileName(f);
item.filePath = f;
//Console.WriteLine(“name:” f “,path:” item.filePath);
// 加到列表框显示
this.listFile.Items.Add(item);
}
}
// 默认打开第一个文件显示(手动SetSelect也会触发IndexChanged事件)
if (listFile.Items.Count > 0)
listFile.SetSelected(0, true);
}
private void listFile_SelectedIndexChanged(object sender, EventArgs e)
{
PictureListItem item = (PictureListItem)listFile.SelectedItem;
if (item == null) return;
//加载图片
pathPic.Load(item.filePath);
}
}
// 文件项
class PictureListItem
{
public string name;
public string filePath;
public override string ToString()
{
return name;
}
}
}
五.菜单栏和工具栏
1.菜单栏 MenuStrip:窗口顶部停靠菜单栏
(1)使用:设计界面可视化设计,可以添加菜单项、下拉框、分割线、文本
2.工具栏 ToolStrip:窗口顶部停靠工具栏,一般位于菜单栏下方(图标栏)
(1)使用:设计界面可视化设计,可以添加工具按钮、分割线等。一般窗口上方第一排为菜单栏,第二排为工具栏,二者对应
3.右键菜单(上下文菜单):ContextMenuStrip
(1)定义:在窗口不同部位右键显示的菜单栏应该具有不同的选项,一般项目中菜单栏和工具栏可能没有,但是右键菜单是一定具有的!
(2)使用:
– 在设计界面添加 ContextMenuStrip上下文菜单,并添加菜单项。但此时仅是对上下文菜单进行了定义和声明,右键还不能显示
(1)界面设计
(2)Form.cs逻辑代码
namespace WindowsFormsApp_learning
{
public partial class Form5 : Form
{
public Form5()
{
InitializeComponent();
}
private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
//IndexFromPoint:返回从指定位置处开始的第一个Item索引
int index = listBox1.IndexFromPoint(e.Location);
//如果index>=0,则说明右键了某个Item项而不是空白位置
if (index >= 0)
{
listBox1.SetSelected(index, true);//选中该项
Item_Del.Enabled = true;
}
else
{
listBox1.ClearSelected();//清空
Item_Edit.Enabled = false;//禁用
Item_Del.Enabled = false;
//Item_Edit.Visible = false;//直接不显示该项
//Item_Del.Visible = false;
}
this.contextMenu.Show(listBox1, e.Location);//显示右键菜单
}
}
private void Item_Add_Click(object sender, EventArgs e)
{
MessageBox.Show(“上下文菜单:选择了添加…”);
}
}
}
六.列表视图控件 ListView
1.ListView基本使用
1).列表控件ListView:升级版的ListBox,其显示类似于windows的文件夹视图
(3)简单使用步骤:
– 在设计界面将ListView拖入,并设置Dock为占满(接下来可以在设计界面进行可视化设计,也可以通过代码来添加设计数据项)
– 代码方式设计ListView(推荐):
a.设置显示方式
b.设置列名
c.添加数据项
d.设置每个数据项的子项数据
– 推荐使用代码方式添加数据的原因:很多时候我们并不知道要遍历的项有多少,所以不能提前设计好。只能通过代码方式动态添加
(1)ListView基本用法展示
界面设计非常简单,就是拖入一个ListView控件,并设置Dock为Fill填充满父容器,无其他控件,这里就不再展示设计界面了。基本用法主要展示如何在ListView中使用代码方式添加列名、数据项等。
namespace WindowsFormsApp_learning2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//初始化ListView控件
InitializeListView();
}
private void InitializeListView()
{
//1.设置显示模式为详情模式:显示图标 主项 所有子项
listView1.View = View.Details;
// 设置整行选中(选中某一项的时候选中整行,而不只是主项)
listView1.FullRowSelect = true;
//2.设置列名Add(列名,宽度[像素值],对齐方式)
// 宽度值-2表示自动调整宽度
listView1.Columns.Add(“文件名”,-2,HorizontalAlignment.Left);
listView1.Columns.Add(“修改时期”, 150, HorizontalAlignment.Left);
listView1.Columns.Add(“类型”, 100, HorizontalAlignment.Left);
listView1.Columns.Add(“大小”, -2, HorizontalAlignment.Left);
//3.添加数据项 ListViewItem(数据名称,图标下标)
ListViewItem item1 = new ListViewItem(“Java学习指南.pdf”, 0);
//4.设置子项数据(对应每列的数据)
item1.SubItems.Add(“2020-2-20 12:10”);
item1.SubItems.Add(“PDF”);
item1.SubItems.Add(“192 KB”);
listView1.Items.Add(item1);
}
}
}
(2)ListView基本使用小练习:本地文件目录列表展示
1.ListView控件高级使用:本地文件可视化
(1)在设计界面,将ListView控件拖入,并设置Dock为填充Fill
(2)添加数据项图标的图片资源到Resources
(3)在代码中初始化ListView基本配置,包括列名、显示模式、图标列表等。InitListView()
namespace WindowsFormsApp_learning2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
//初始化ListView控件
InitListView();
//加载添加文件数据项
LoadDir(new DirectoryInfo(“D:\\”));
}
//1.初始化ListView控件
private void InitListView()
{
//设置显示模式为详情模式
listView1.View = View.Details;
//设置整行选中
listView1.FullRowSelect = true;
//设置列名
listView1.Columns.Add(“名称”, -2, HorizontalAlignment.Left);
listView1.Columns.Add(“修改时间”, 150, HorizontalAlignment.Left);
listView1.Columns.Add(“类型”, 100, HorizontalAlignment.Left);
listView1.Columns.Add(“大小”, -2, HorizontalAlignment.Left);
//创建ImageList(提供一些方法,来管理一些列图片资源的列表类),并添加2个小图标
ImageList imgList = new ImageList();
imgList.ImageSize = new Size(16, 16);//设置列表中图片的大小
imgList.Images.Add(Properties.Resources.Icon_file);//事先添加到Resources的图标
imgList.Images.Add(Properties.Resources.Icon_folder);
// 设置 SmallImageList 用于显示小图标(适用于小图标、详情、列表模式显示图标)
// 设置 LargeImageList 用于显示大图标(适用于大图标模式显示图标)
listView1.SmallImageList = imgList;
}
//2.遍历本地资源,获取文件或目录
private void LoadDir(DirectoryInfo dir)
{
// 加载优化:BeginUpdate() EndUpdate(),该方法指明内部的程序全部执行完后才进行一次总更新,而不是每Add一项就更新一次。(避免频繁刷新)
// listView1.BeginUpdate();
// 获取[子目录]列表
DirectoryInfo[] subDirs = dir.GetDirectories();
foreach (DirectoryInfo d in subDirs)
{
//如果该目录是隐藏文件,则跳过
if ((d.Attributes & FileAttributes.Hidden) > 0) continue;
//否则就添加到ListVie中显示(目录名称,最新修改时间,数据项类型,文件大小)
AddListItem(d.Name, d.LastWriteTime, “文件夹”, -1);
}
// 获取[子文件]列表
FileInfo[] subFiles = dir.GetFiles();
foreach (FileInfo f in subFiles)
{
//如果该文件是隐藏文件,则跳过
if ((f.Attributes & FileAttributes.Hidden) > 0) continue;
string ext = f.Extension.ToUpper(); // 获取文件拓展名后缀(.zip、.png、.doc等)
//否则就添加到ListVie中显示(文件名称,最新修改时间,文件后缀类型,文件大小)
AddListItem(f.Name, f.LastWriteTime, ext, f.Length);
}
// listView1.EndUpdate();
}
//3,将文件和目录按照不同类型添加到ListView数据项
private void AddListItem(string label, DateTime time, string type, long size)
{
// 判断是文件还是文件夹,使用不同的图标
int imageIndex = 0;
if (!type.Equals(“文件夹”)) imageIndex = 1;
ListViewItem item = new ListViewItem(label, imageIndex);//imageIndex会去listView下的SmallImageList找图标
// 添加子项 – 时间
item.SubItems.Add(time.ToString(“yyyy-MM-dd HH:mm”));
// 添加子项 – 类型
item.SubItems.Add(type);
// 添加子项 – 文件大小
string sizeStr = “”;
if (size < 0)
sizeStr = “”; // 文件夹不显示大小
else if (size < 1000)
sizeStr = “” size;
else if (size < 1000000)
sizeStr = size / 1000 ” KB”;
else if (size < 1000000000)
sizeStr = size / 1000000 ” MB”;
else
sizeStr = size / 1000000000 ” GB”;
item.SubItems.Add(sizeStr);
listView1.Items.Add(item);
}
}
}
2.ListView模式切换
1).ListView控件:模式切换(切换详情、列表、大图标、小图标模式显示)
(1)设计要求:在上节内容文件目录展示案例的基础上,实现右键菜单切换显示模式
(2)开发流程:
– 在设计界面,拖入ListView控件,并设置Dock为充满Fill
– 在代码中初始化ListView控件列名、[大图标列表]、[小图标列表]等(因为大图标模式和小图标模式需要使用两套图标)
– 加载遍历文件和目录,并添加数据项Item
– 右键菜单实现切换模式功能
namespace WindowsFormsApp_learning2
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//1.初始化ListView控件
InitListView();
//2.载入文件和目录
LoadDir(new DirectoryInfo(“D:\\”));
}
//1.初始化ListView控件
private void InitListView()
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
//设置列名
listView1.Columns.Add(“名称”, -2, HorizontalAlignment.Left);
listView1.Columns.Add(“修改时间”, 150, HorizontalAlignment.Left);
listView1.Columns.Add(“类型”, 100, HorizontalAlignment.Left);
listView1.Columns.Add(“大小”, -2, HorizontalAlignment.Left);
//*创建SmallImageList 小图标列表(用于Detail、SmallIcon等模式里显示小图标)
ImageList smallImgList = new ImageList();
smallImgList.ImageSize = new Size(16, 16);//设置列表中图片的大小
smallImgList.Images.Add(Properties.Resources.Icon_file);
smallImgList.Images.Add(Properties.Resources.Icon_folder);
listView1.SmallImageList = smallImgList;
//*创建LargeImageList 大图标列表(用于LargeIcon模式里显示大图标)
ImageList largeImgList = new ImageList();
largeImgList.ImageSize = new Size(64, 64);//设置列表中图片的大小
largeImgList.Images.Add(Properties.Resources.Icon_file2);
largeImgList.Images.Add(Properties.Resources.Icon_folder2);
listView1.LargeImageList = largeImgList;
}
//2.加载数据项:遍历文件和目录,按照不同类型添加数据项Item及其子项
private void LoadDir(DirectoryInfo dir)
{
listView1.BeginUpdate();
// 获取[子目录]列表
DirectoryInfo[] subDirs = dir.GetDirectories();
foreach (DirectoryInfo d in subDirs)
{
if ((d.Attributes & FileAttributes.Hidden) > 0) continue;
AddListItem(d.Name, d.LastWriteTime, “文件夹”, -1);
}
// 获取[子文件]列表
FileInfo[] subFiles = dir.GetFiles();
foreach (FileInfo f in subFiles)
{
if ((f.Attributes & FileAttributes.Hidden) > 0) continue;
string ext = f.Extension.ToUpper();//文件后缀名
AddListItem(f.Name, f.LastWriteTime, ext, f.Length);
}
listView1.EndUpdate();
}
//3,将文件和目录按照不同类型添加到ListView数据项
private void AddListItem(string label, DateTime time, string type, long size)
{
// 判断是文件还是文件夹,使用不同的图标
int imageIndex = 0;
if (!type.Equals(“文件夹”)) imageIndex = 1;
ListViewItem item = new ListViewItem(label, imageIndex);
// 添加子项 – 时间
item.SubItems.Add(time.ToString(“yyyy-MM-dd HH:mm”));
// 添加子项 – 类型
item.SubItems.Add(type);
// 添加子项 – 文件大小
string sizeStr = “”;
if (size < 0)
sizeStr = “”; // 文件夹不显示大小
else if (size < 1000)
sizeStr = “” size;
else if (size < 1000000)
sizeStr = size / 1000 ” KB”;
else if (size < 1000000000)
sizeStr = size / 1000000 ” MB”;
else
sizeStr = size / 1000000000 ” GB”;
item.SubItems.Add(sizeStr);
listView1.Items.Add(item);
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
View view = listView1.View;
//根据显示模式,设置右键菜单的选中项
menuDetail_Item.Checked = (view == View.Details);
menuList_Item.Checked = (view == View.List);
menuLarge_Item.Checked = (view == View.LargeIcon);
menuSmall_Item.Checked = (view == View.SmallIcon);
//显示右键菜单(在鼠标右键位置e.Location)
contextMenuStrip.Show(listView1, e.Location);
}
}
private void menuDetail_Item_Click(object sender, EventArgs e)
{
listView1.View = View.Details;
}
private void menuList_Item_Click(object sender, EventArgs e)
{
listView1.View = View.List;
}
private void menuLarge_Item_Click(object sender, EventArgs e)
{
listView1.View = View.LargeIcon;
}
private void menuSmall_Item_Click(object sender, EventArgs e)
{
listView1.View = View.SmallIcon;
}
}
}
3.ListView数据项列排序
(1)编码步骤(在文件目录展示案例的基础上):
– 添加箭头图标资源,表示升序和降序标识
– 将图标加入SmallImageList,其中Images.Add (key, image ) 表示添加一个图像image,并关联一个key值。后面可以根据key值获取此图片。
– 添加数据项Tag:每个ListView可以关联一个Tag,一般为一个对象。用于排序的时候便于直接使用对象做比较
– 定义比较器MyListItemSorter,实现绑定Tag对象的自定义排序
(1)自定义Tag对象,封装信息
namespace WindowsFormsApp_learning2
{
// 每个列表项关联的Tag数据
class MyListItemTag
{
public int type = 0; //类型:文件夹0, 文件1
public string path;
public string name;
public DateTime time;
public long size = -1;
public string ext; // 显示文件后缀/文件夹
public MyListItemTag(int type, string path, string name, DateTime time, long size, string ext)
{
this.type = type;
this.path = path;
this.name = name;
this.time = time;
this.size = size;
this.ext = ext;
}
}
}
(2)自定义比较器,实现对象排序(传入两个Tag对象)
namespace WindowsFormsApp_learning2
{
//继承IComparer接口,实现对象比较方法
public class MyListItemSorter : IComparer
{
public bool asc = true;//是否升序排序
public MyListItemSorter(bool asc)
{
this.asc = asc;
}
public int Compare(object x, object y)
{
//按列排序时,传入的是两个ListViewItem对象
ListViewItem item1 = (ListViewItem)x;
ListViewItem item2 = (ListViewItem)y;
// Tag: 每一项关联的数据
MyListItemTag tag1 = (MyListItemTag)item1.Tag;
MyListItemTag tag2 = (MyListItemTag)item2.Tag;
// 目录在前, 文件在后
if (tag1.type != tag2.type)
return CompareInt(true, tag1.type, tag2.type);
// 按名字比较
return CompareStringIgnoreCase(asc, tag1.name, tag2.name);
}
// 两个 Bool 值的比较
public int CompareBool(bool asc, bool x, bool y)
{
int xx = x ? 1 : 0;
int yy = y ? 1 : 0;
return CompareInt(asc, xx, yy);
}
// 两个 Int 值的比较
public int CompareInt(bool asc, int x, int y)
{
if (asc)
return x – y;
else
return y – x;
}
// 两个String值的比较
public int CompareString(bool asc, string x, string y)
{
if (asc)
return x.CompareTo(y);
else
return y.CompareTo(x);
}
// 两个String值的比较 (不区分大小写)
public int CompareStringIgnoreCase(bool asc, string x, string y)
{
return CompareString(asc, x.ToLower(), y.ToLower());
}
}
}
(3)主窗体Form.cs逻辑代码
namespace WindowsFormsApp_learning2
{
public partial class Form4 : Form
{
// 当前默认排序:升序
private bool asc = true;
public Form4()
{
InitializeComponent();
InitListView();
LoadDir(new DirectoryInfo(“D:\\”));
}
//1.初始化ListView控件
private void InitListView()
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
//设置列名
listView1.Columns.Add(“名称”, -2, HorizontalAlignment.Left);
listView1.Columns.Add(“修改时间”, 150, HorizontalAlignment.Left);
listView1.Columns.Add(“类型”, 100, HorizontalAlignment.Left);
listView1.Columns.Add(“大小”, -2, HorizontalAlignment.Left);
//创建smallImageList,添加小图标资源
ImageList imgList = new ImageList();
imgList.ImageSize = new Size(16, 16);//设置列表中图片的大小
imgList.Images.Add(Properties.Resources.Icon_file);
imgList.Images.Add(Properties.Resources.Icon_folder);
listView1.SmallImageList = imgList;
// 把图标资源加到 SmallImageList 里,供列标题头部显示
imgList.Images.Add(“Sort_ASC”, Properties.Resources.up);
imgList.Images.Add(“Sort_DESC”, Properties.Resources.down);
//ListView首列图标显示为 key=””Sort_ASC”
listView1.Columns[0].ImageKey = “Sort_ASC”;
}
//2.遍历本地资源,获取文件或目录
private void LoadDir(DirectoryInfo dir)
{
listView1.BeginUpdate();
// 获取[子目录]列表
DirectoryInfo[] subDirs = dir.GetDirectories();
foreach (DirectoryInfo d in subDirs)
{
if ((d.Attributes & FileAttributes.Hidden) > 0) continue;
//**将数据打包为Tag对象进行添加
MyListItemTag tag = new MyListItemTag(0,d.FullName,d.Name,d.LastWriteTime,-1,”文件夹”);
AddListItem(tag);
}
// 获取[子文件]列表
FileInfo[] subFiles = dir.GetFiles();
foreach (FileInfo f in subFiles)
{
if ((f.Attributes & FileAttributes.Hidden) > 0) continue;
//**将数据打包为Tag对象进行添加
MyListItemTag tag = new MyListItemTag(1, f.FullName, f.Name, f.LastWriteTime, f.Length, f.Extension.ToUpper());
AddListItem(tag);
}
listView1.EndUpdate();
}
//3,将文件和目录按照不同类型添加到ListView数据项
private void AddListItem(MyListItemTag tag)
{
// 判断是文件还是文件夹,使用不同的图标
int imageIndex = 0;
if (!tag.type.Equals(“文件夹”)) imageIndex = 1;
ListViewItem item = new ListViewItem(tag.name, imageIndex);
// **Item关联Tag数据对象(方便我们从Item中获取数据、进行排序等)
item.Tag = tag;
// 事件子项
item.SubItems.Add(tag.time.ToString(“yyyy-MM-dd HH:mm”));
// 后缀名称子项
item.SubItems.Add(tag.ext);
// 大小子项
long size = tag.size;
string sizeStr = “”;
if (size < 0)
sizeStr = “”; // 文件夹不显示大小
else if (size < 1000)
sizeStr = “” size;
else if (size < 1000000)
sizeStr = size / 1000 ” KB”;
else if (size < 1000000000)
sizeStr = size / 1000000 ” MB”;
else
sizeStr = size / 1000000000 ” GB”;
item.SubItems.Add(sizeStr);
listView1.Items.Add(item);
}
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
{
//切换当前排序
this.asc = !asc;
if (asc)
{
//设置排序器
listView1.ListViewItemSorter = new MyListItemSorter(true);
listView1.Sort();//排序
listView1.Columns[0].ImageKey = “Sort_ASC”;//切换图标
}
else
{
listView1.ListViewItemSorter = new MyListItemSorter(false);
listView1.Sort();
listView1.Columns[0].ImageKey = “Sort_DESC”;
}
}
}
}
}
(1)设计ListView显示,并添加数据项
a.添加右键菜单,新建菜单项-重命名
(4)注意:
namespace WindowsFormsApp_learning2
{
public partial class Form5 : Form
{
// 当前鼠标事件点中的项
private ListViewItem mouseClickItem;
public Form5()
{
InitializeComponent();
InitListView();
// 初始化添加几项数据
AddListItem(new Student(20201001, “shao”, “13810012xxx”));
AddListItem(new Student(20201002, “wang”, “18799122xxx”));
AddListItem(new Student(20201003, “li”, “13490912xxx”));
}
//1.设计ListView显示,并添加数据项
private void InitListView()
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.LabelEdit = true;
// 设置列名
// 宽度值-2表示自动调整宽度
listView1.Columns.Add(“姓名”, 150, HorizontalAlignment.Left);
listView1.Columns.Add(“学号”, 150, HorizontalAlignment.Left);
}
private void AddListItem(Student stu)
{
ListViewItem item = new ListViewItem(stu.Name, 0);
item.Tag = stu;
item.SubItems.Add(stu.Id “”);
item.SubItems.Add(stu.Phone);
listView1.Items.Add(item);
}
//2.右键显示右键菜单
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
ListViewItem item = listView1.GetItemAt(e.X, e.Y);
this.mouseClickItem = item;
//判断是否可以重命名该项
renameMenu_Item.Enabled = (item != null);
//展示该位置/该项 的 右键菜单
contextMenuStrip1.Show(listView1, e.Location);
}
}
private void renameMenu_Item_Click(object sender, EventArgs e)
{
this.mouseClickItem.BeginEdit();
}
private void listView1_BeforeLabelEdit(object sender, LabelEditEventArgs e)
{
}
private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
int index = e.Item;
string label = e.Label;
// 检查名称是否冲突
for (int i = 0; i < listView1.Items.Count; i )
{
if (index == i) continue;
if (label == listView1.Items[i].Text)
{
e.CancelEdit = true;
MessageBox.Show(“名字重复”, “提示”);
return;
}
}
// 否则就没有重复名称,接受新的更改
e.CancelEdit = false;
// 同步更新后台数据(对象为引用数据)
Student stu = (Student)listView1.Items[index].Tag;
stu.Name = label;
}
}
}
七.表格视图控件 DataGridView
1.表格视图基本用法
1).表格视图 DataGridView 基础: 以单元格表格的形式展示数据和操作
(1)在设计界面,拖入DataGridView控件,并设置Dock为填满Fill
(2)在设计界面或代码中,设置列名Columns
(3)在设计界面或代码中添加数据项,推荐代码添加(动态拓展)
namespace WindowsFormsApp_learning2
{
public partial class Form6 : Form
{
public Form6()
{
InitializeComponent();
//初始化表格视图布局
InitGridView();
//初始化添加数据
AddRows(new Student(202101, “wx”, “18264886xxx”));
AddRows(new Student(202102, “my”, “13012459xxx”));
AddRows(new Student(202103, “tom”, “19857138xxx”));
}
//1.初始化列数、列名
private void InitGridView()
{
//设置表格显示列数
this.dataGridView1.ColumnCount = 4;
//设置每列的列头名称
dataGridView1.Columns[0].Name = “学号”;
dataGridView1.Columns[1].Name = “姓名”;
dataGridView1.Columns[2].Name = “性别”;
}
//2.添加表格数据项
private void AddRows(Student stu)
{
//设置Object数组!代表一行数据
Object[] row =
{
stu.Id,stu.Name,”男”,stu.Phone
};
//添加数据行
dataGridView1.Rows.Add(row);
}
}
}
2.表格视图高级用法
1).表格视图 DataGridView 基本操作:属性、增删改查
(1)DataGridView 常用属性:
– [杂项] Columns : 设置列相关的列数、列名等
– [外观] ColumnHeadersVisible : 设置列头是否可见(默认为True)
– [外观] RowHeadersVisible : 设置行的头是否可见(默认为True)
– [行为] MultiSelect :是否允许多项选择(默认为True)
– [行为] AllowUserToxxx:是否允许用户对单元格进行 增删改查等修改
(2)DataGridView 常用操作:
– 添加一行数据:grid.Rows.Add(Object[]) 或 界面手动添加(需要触发事件,持久化到数据库)
– 获取行数据:
a.索引方式:grid[col,row] 注意是列索引在前,行索引在后
b.函数方式:grid.Rows[i].Cells[j]
– 删除行数据:
a.单行删除:grid.Rows.RemoveAt(int i):删除第i行数据
b.多行删除(ctrl多选):grid.Rows.Remove(DataGridViewRow row):删除指定的row对象
(1)实现方式:
(2)相关属性:ReadOnly
– 代码中:grid.Columns[0].ReadOnly = True;
(1)案例界面设计
(2)Form.cs主窗体逻辑代码
namespace WindowsFormsApp_learning2
{
public partial class Form7 : Form
{
public Form7()
{
InitializeComponent();
InitGridView();
AddRows(new Student(202101, “wx”, “18264886xxx”));
AddRows(new Student(202102, “my”, “13012459xxx”));
AddRows(new Student(202103, “tom”, “19857138xxx”));
}
//1.初始化列数、列名
private void InitGridView()
{
this.grid.ColumnCount = 4;
grid.Columns[0].Name = “学号”;
grid.Columns[1].Name = “姓名”;
grid.Columns[2].Name = “性别”;
}
//2.添加表格数据项
private void AddRows(Student stu)
{
Object[] row =
{
stu.Id,stu.Name,”男”,stu.Phone
};
grid.Rows.Add(row);
}
private void btn_add_Click(object sender, EventArgs e)
{
Object[] row = new object[4];
row[0] = 202104;
row[1] = “testUser”;
row[2] = “女”;
row[3] = “1928373838”;
grid.Rows.Add(row);
}
private void btn_get_Click(object sender, EventArgs e)
{
//获取第0行数据
Object id = grid[0, 0].Value;
Object name = grid[1, 0].Value;
Object sex = grid.Rows[0].Cells[2].Value;
Object phone = grid.Rows[0].Cells[3].Value;
//显示在MessageBox
MessageBox.Show(id “,” name “,” sex “,” phone);
}
private void btn_del_Click(object sender, EventArgs e)
{
//单行删除 删除第0行
//grid.Rows.RemoveAt(0);
//多行删除 删除[选中]的所有行
foreach(DataGridViewRow row in grid.SelectedRows)
{
grid.Rows.Remove(row);
}
}
private void grid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.ColumnIndex == 1)
{
}else if (e.ColumnIndex == 2)
{
}
else if(e.ColumnIndex == 3)
{
string str = e.FormattedValue.ToString();
if(str.Length != 11)
{
return;
}
foreach(char ch in str)
{
if(ch < ‘0’ || ch > ‘9’)
{
return;
}
}
}
}
//7.单元格验证完成后执行的事件
private void grid_CellValidated(object sender, DataGridViewCellEventArgs e)
{
}
}
}