winform下拉框控件(文本框下拉选项设置方法)

第一部分: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,启动项目,运行初始窗口

winform下拉框控件(文本框下拉选项设置方法)

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)

{

}

}

}

发表评论

登录后才能评论