当前位置:首页 >> 计算机软件及应用 >>

CSharp_WinForm实践开发教程课件(全)_图文

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨a 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

课程地位
Computer Base 综合基础课程 Database Base OOP\Java HTML&JavaScript C

SQL Server

C# WinForms

JSP/Servlet

Testing/SQA

Oracle ASP.NET Ajax [Javascript&XML]

XML

EJB/WebService Struts/JSF RSS

Linux

第一章、Windows编程基础

CONTENTs
?本章主要内容介绍
1.1 1.2 1.3 windows和窗体 Visual Stutio .net IDE简介 事件处理

1.1 Windows和窗体
本章学习目标: ? 理解 Windows 窗体 ? 使用基本控件如标签、文本、按钮、列表框 和组合框 ? 掌握窗体的常用属性和方法

1.1 Windows和窗体
GUI界面

控件

1.1 Windows和窗体
各种控件 放置控件的区域 属性

1.1 Windows和窗体
System.Windows.Forms
简单而强大 改善了接口和基类 IntelliSense 新的管理数据提供程序 安全 灵活的控件 通晓数据 向导

WinForms应用程序可能存在多个窗体,用于获取用户输入的 数据和向用户显示数据

1.1.2 创建 WinForms应用程序
“开始”?“程序”?“Microsoft Visual Studio.NET 2005”?“Microsoft Visual Studio.NET 2005”

创建 WinForms应用程序 6-2
设计窗口

1.1.2

创建 WinForms应用程序

基础核心命名空间 using System; 提供了大量绘图工具的访问权限 using System.Drawing; ArrayList、BitArray、Hashtable、Stack、 using System.Collections; StringCollection 和 StringTable 类 using System.ComponentModel; using System.Windows.Forms; 大量窗体和控件 namespace SampleProject { /// <summary> /// Form1 的摘要说明。 /// </summary> public class Form1 : System.Windows.Forms.Form { /// <summary> /// 必需的设计器变量. /// </summary>

从 System.Windows.Forms.Form 派生

Visual Studio .NET 生成的代码

1.1.2 创建 WinForms应用程序
private System.ComponentModel.Container components = null; 项目的容器 public Form1() { // // Windows 窗体设计器支持所必需的 // InitializeComponent(); // // TODO:在 InitializeComponent 调用之后 添加任何构造函数代码 // // 下面代码见: Form1.Designer.cs文件 } private void InitializeComponent() 构造函数调用 InitializeComponent() 方法 { this.components = new System.ComponentModel.Container(); this.Size = new System.Drawing.Size(300,300); this.Text = "Form1"; }

创建 WinForms应用程序
/// <summary> /// 清理所有正在使用的资源。【下面代码: Form1.Designer.cs 】 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } 释放系统资源

1.1.2 创建 WinForms应用程序
程序的主入口点

//下面代码见:program.cs文件 [STAThread] static void Main() { Application.Run(new Form1()); }

1.1.3 WinForms 中的常用控件
System.Windows.Forms.Control

可视化界面组件统称为控件
System.Windows.Forms

Control TextBoxBase ButtonBase Button CheckBox RadioButton Label ListControl TextBox

ComboBox

ListBox

1.1.3 WinForms 中的常用控件

文本框

组合框

标签

列表框

按钮

1.1.3 WinForms 中的常用控件
标签
属性
标签控件 按钮控件 文本框控件

说明 该属性用于设置或获取与该控件关联的文本 说明 隐藏控件,调用该方法时,即使 Visible 属 性设置为 True,控件也不可见 相当于将控件的 Visible 属性设置为 True 并显示控件 说明 用户单击控件时将发生该事件

Text 方法 Hide

Show
列表控件

事件
组合框控件

Click

案例:窗口的打开和关闭
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { linkLabel1.LinkVisited = true; Form2 newform = new Form2(); newform.Show(); this.Hide(); } private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { //label2.Visible = true; label2.Show(); } private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { label2.Visible = false; label2.Hide(); }

1.1.3 WinForms 中的常用控件
文本框 属性
MaxLength Multiline

说明
可在文本框中输入的最大字符数 表示是否可在文本框中输入多行 文本

Passwordchar
ReadOnly Text

机密和敏感数据,密码输入字符
文本框中的文本为只读 检索在控件中输入的文本

方法
Clear

说明
删除现有的所有文本

事件
KeyPress

说明
用户按一个键结束时将发生该事件

1.1.3 WinForms 中的常用控件
按钮

属性
Enabled

说明
确定是否可以启用或禁用该控件

方法
PerformClick

说明
Button 控件的 Click 事件

事件
Click

说明
单击按钮时将触发该事件

private void button2_Click(object sender, EventArgs e) { clear(); } private void button1_Click(object sender, EventArgs e) { if (textBox1.Text == string.Empty || textBox2.Text == string.Empty) { MessageBox.Show("信息禁止为空!","登录提示"); clear(); return; } if (!textBox1.Text.Equals("admin") || !textBox2.Text.Equals("admin")) { MessageBox.Show("用户名称或密码为空!", "登录提 示"); clear(); return; } else { MessageBox.Show("欢迎您登录本系统!","消息提示"); clear(); } } public void clear() { textBox1.Clear(); textBox2.Clear(); textBox2.Focus(); }

案例:用户登录设计

1.1.3 WinForms 中的常用控件
列表框

属性
Items

SelectionMode SelectedIndex SelectedItem SelectedItems Text

方法
ClearSelected

事件
SelectedIndexChanged

使用列表框(1)
private void Form1_Load(object sender, EventArgs e) { this.listBox1.Items.Add("软件部"); this.listBox1.Items.Add("硬件部"); this.listBox1.Items.Add("财务部"); this.listBox1.Items.Add("人事部"); } private void listBox1_SelectedIndexChanged(object sender, EventArgs e) { MessageBox.Show("您选择的部门是: "+listBox1.SelectedItem.ToString()+",位列第 "+listBox1.SelectedIndex.ToString(),"信息提示"); }

使用列表框(2)
private void button1_Click(object sender, EventArgs e) { listBox1.Items.Clear(); listBox1.Items.Add("软件部"); listBox1.Items.Add("硬件部"); listBox1.Items.Add("财务部"); listBox1.Items.Add("人事部"); } private void button2_Click(object sender, EventArgs e) { listBox1.Items.Insert(2,"插入值"); label1.Text = "已经添加" + listBox1.Items.Count.ToString() + "条记录"; }

1.1.3 WinForms 中的常用控件
组合框

属性
DropDownStyle

说明
ComboBox 控件的样式

MaxDropDownItems 下拉区显示的最大项目数

方法
Select

说明
在 ComboBox 控件上选定指定 范围的文本

使用组合框
private void Form1_Load(object sender, EventArgs e) { this.comboBox1.Items.Add("财务部"); this.comboBox1.Items.Add("产品部"); this.comboBox1.Items.Add("销售部"); this.comboBox1.Items.Add("生产部"); //默认的选择是"产品部" this.comboBox1.SelectedIndex = 1;
this.comboBox2.Items.Add("财务部"); this.comboBox2.Items.Add("产品部"); this.comboBox2.Items.Add("销售部"); this.comboBox2.Items.Add("生产部"); //默认的选择是"产品部" this.comboBox2.SelectedIndex = 1; this.comboBox3.Items.Add("财务部"); this.comboBox3.Items.Add("产品部"); this.comboBox3.Items.Add("销售部"); this.comboBox3.Items.Add("生产部"); //默认的选择是"产品部" this.comboBox3.SelectedIndex = 1;

}

1.1.3 WinForms 中的常用控件
消息框用于显示消息

消息框窗口

MessageBox.Show(“[消息文本]");
if (MessageBox.Show(“保存文件”,“保存", MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxIcon MessageBoxDefaultButton.Button1) == DialogResult.Yes) { //保存文件所用的代码 //保存后的 MessageBox Default Button - YES YesNo Buttons }

Abort, Cancel, Ignore, No, None, Ok, Retry 和 Yes

消息框窗口
重载方法
Show(string text); Show(string text, string caption); Show(string text, string caption, MessageBoxButtons buttons); Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon);

……

private void button1_Click(object sender, EventArgs e) { MessageBox.Show("嘿,这是简单提示!","信息提示"); } private void button2_Click(object sender, EventArgs e) { DialogResult result = MessageBox.Show("嘿,这是问询提示! ","问询提示",MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { label1.Text = "您选择了YES";
private void button3_Click(object sender, EventArgs e) { DialogResult result = MessageBox.Show("嘿,这是带有图标的问询 提示!", "问询提示", MessageBoxButtons.YesNoCancel,MessageB oxIcon.Question,MessageBoxDefaultButton.Bu tton3,MessageBoxOptions.RightAlign); if (result == DialogResult.Yes) { label1.Text = "您选择了图标YES"; } else if(result==DialogResult.Cancel) { label1.Text = "您选择了图标取消"; } else if (result == DialogResult.No) { label1.Text = "您选择了图标NO"; } }

} else { label1.Text = "您选择了NO"; } }

应用程序示例
工具箱 属性 解决方案资 源管理器 窗口

应用程序示例
btnCancel_Click(object sender, System.EventArgs private void btnAdd_Click(object sender, System.EventArgs e) e) { private void btnExit_Click (object sender, System.EventArgs e) this.txtEmpName.Enabled=true; this.txtEmpName.Text=""; { this.txtAddress.Text=""; this.txtAddress.Enabled=true; string str=""; this.cboDesignation.Enabled=true; this.cboDesignation.Text=“ 经理"; for(int ctr=0;ctr<=this.lstCurrDeptName.SelectedItems.Count-1; ctr++) } this.lstCurrDeptName.Enabled=true; private void btnAdd_Click(object sender, System.EventArgs e) str += "\n"+this.lstCurrDeptName.SelectedItems[ctr].ToString(); }{ MessageBox.Show(“选定的项目为\n" +str); Application.Exit(); }}

应用程序示例
在退出应用程序之前,使用 MessageBox.Show() 显示在 str 变量中 存储选定项的消息框

private void cboDesignation_SelectedIndexChanged (object sender, System.EventArgs e) { MessageBox.Show(“您已经选定了" + this.cboDesignation.SelectedItem.ToString()); }

1.1.4
图标

窗体容器简介
System.Windows.Forms
标题栏

系统按钮

Control

ScrollableControl 控件

ContainerControl

Form

1.1.4
? ?

窗体容器简介

SDI [单文档界面] MDI [多文档界面]

1.1.5 窗体的属性
属性
StartPosition CancelButton
初始窗口位置 按下esc键后执行那个按钮



ControlBox 确定系统是否有图标和最大最小关闭按钮。 FormBorderStyle 指定边框和标题栏的外观和行为。 HelpButton 确定窗体的标题栏上是否有帮助按钮。 KeyPreview 确定窗体键盘事件是否已经向窗体注册。 MainMenuStrip 确定键盘激活和多文档合并。 ShowInTaskbar 确定窗体是否出现在任务栏中。 WindowState 确定窗体的初始可视状态。

1.1.5

窗体的常用方法和事件
方法

Activate 当窗体被激活时候发生 MdiChildActivate 当MDI子窗体被激活时候发生 事件 Activated Closed Closing Load

示例:显示另一窗体

示例:显示另一窗体
[被调用的窗体类] [窗体实例] = new [被调用的窗体类]();
[窗体实例].Show();下面,在菜单的单击事件中写下如下的事件。
private void menuItem3_Click(object sender, EventArgs e) { Form2 Mdichild = new Form2(); Mdichild.MdiParent = this; Mdichild.Show(); }

当然,需要再建立两个窗体对象,form2和form3窗体

示例1:在form2窗体中进行如下操作

单击“发送”按 钮

应用程序示例
首先,将form2的comboBox1下拉框填充完毕后,增加load事件
private void Form2_Load(object sender, EventArgs e) { comboBox1.SelectedIndex = 0; textBox3.Text = ""; textBox1.Focus(); }

其次,添加form2的发送信息事件
public void button1_Click(object sender, EventArgs e) { if (textBox1.Text == "" || textBox2.Text == "" || comboBox1.Text == "") { MessageBox.Show("姓名,或者邮件,或者提交,信息禁止为空!", "信息提示"); } else { this.Hide(); Form3 childform3 = new Form3(this.textBox1.Text,this.textBox2.Text,this.comboBox1.SelectedItem.ToString(),this.textBox3.Text); childform3.Show(); } }

最后关闭窗体事件:
private void button2_Click(object sender, EventArgs e) { this.Close(); }

对于form3窗体而言,在系统初始事件填写如下代码:
public partial class Form3 : Form { private string _name; private string _emailId; private string _subject; private string _feedBack;

应用程序示例

public Form3(string varName, string varEmail, string varSubject, string varFeedBack) { InitializeComponent(); // 在 private 变量中存储值 this._name = varName; this._emailId = varEmail; this._subject = varSubject; this._feedBack = varFeedBack; // 在列表框中放置值 listBox1.Items.Add("姓名:" + this._name); listBox1.Items.Add("邮件地址:" + this._emailId); listBox1.Items.Add("信息主题:" + this._subject); listBox1.Items.Add("反馈意见:" + this._feedBack); } private void button1_Click(object sender, EventArgs e) { MessageBox.Show("感谢您输入的反馈!"); this.Close(); } }

示例2:在MDI父窗口中,子窗口如何彼此之间传 递信息?

代码见下

示例3:如何防止重复打开子窗体啊?
方法: 直接检测是否已经打开此MDI窗体 // 是否已经打开了?(用循环来判断) foreach (Form childrenForm in this.MdiChildren) { //检测是不是当前子窗体名称 if (childrenForm.Name == "子窗体名称") { //是的话就是把他显示 childrenForm.Visible = true; //并激活该窗体 childrenForm.Activate(); return; } } //下面是打开子窗体 Form1 childrenForm = new Form1(); childrenForm.MdiParent = this; childrenForm.Show(); childrenForm.WindowState = FormWindowState.Maximized;

示例4:另一种窗体之间的传值技巧(一) ——传单个值
1、先在Form2中定义一个成员变量和一个属性如下: private string form2zhi = null; public string Form2ChuanZhi { get { return form2zhi; } } 2、再在Form3中定义一个成员变量和一个属性如下: private string form3zhi = null; public string Form3ChuanZhi { set { form3zhi = value; } get { return form3zhi; } } 3、双击btn_ChuanZhi在这个事件中写入以下代码 (主要是显示Form3窗体和将Form2中的值传过去): Form3 form3 = new Form3(); form3.Form3ChuanZhi = form2zhi;//将值传过去 form3.Show();

代码见下

示例5:另一种窗体之间的传值技巧(二) 代码见下 ——类保存任意值

Winform界面美化技巧
1、从附件资料中确认有第三方动态链接库文件DotNetSkin.dll或者IrisSkin2.dll, 这两个文件是第三方开发设计的Winform界面美化的主要文件

2、打开VS2005,展开工具箱,右键点击界面选择“添加选项卡”,新建选项卡“皮肤

Winform界面美化技巧

3、在工具箱的新建选项卡“皮肤”里面单击右键,选择“选择项”,将展开选择工具箱项

4、在工具箱项窗口点击“浏览”,导入第三方动态 链接库文件DotNetSkin.dll或者IrisSkin2.dll,两个dll 都是一样的用,不同的是DotNetSkin.dll用的皮肤文 件是*.skn,IrisSkin2.dll是用的*.ssk

Winform界面美化技巧
5、则在工具箱的皮肤选项卡内将出现皮肤控件。

5、皮肤文件的基本用法是:拖拽任何一个皮肤控件到某个窗体上面,进行如下的编码:

namespace porjectname { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.skinEngine1.SkinFile = "*.ssk"; 或是 this.skinUI1.SkinFile = "*.skn"; } } }

Winform界面美化技巧
namespace WindowsApplication1 { public partial class Form7 : Form { public Form7() { InitializeComponent(); //this.skinEngine1.SkinFile = "*.ssk"; // string path = Environment.CurrentDirectory + "\\skn皮肤\\LE4-DEFAULT.skn"; this.skinUI1.SkinFile = path; } } 有关ssk文件和skn文件库,请参见附件文件库中相关文件

总结
? WinForms可用于 Windows 窗体应用程序开发 ? Windows 窗体控件是从 System.Windows.Forms.Control 类派生的类 ? 标签控件用于显示用户不能编辑的文本或图像 ? 按钮控件提供用户与应用程序交互的最简便方法 ? 组合框控件是列表框控件和文本框控件的组合, 用户可以键入文本,也可以从所提供的列表中选 择项目 ? 窗体提供了收集、显示和传送信息的界面,是 GUI的重要元素 ? 消息框显示消息,用于与用户交互

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

回顾
? WinForms可用于 Windows 窗体应用程序开发 ? Windows 窗体控件是从 System.Windows.Forms.Control 类派生的类 ? 标签控件用于显示用户不能编辑的文本或图像 ? 按钮控件提供用户与应用程序交互的最简便方法 ? 组合框控件是列表框控件和文本框控件的组合, 用户可以键入文本,也可以从所提供的列表中选 择项目 ? 窗体提供了收集、显示和传送信息的界面,是 GUI的重要元素 ? 消息框显示消息,用于与用户交互

第二章、WinForms控件

CONTENT
?本章主要内容介绍
2.1 2.2 WinForms的高级控件 单(多)文档操作及菜单

2.1 Winforms的高级控件
本节学习目标:
? 使用WinForms中的高级控件 ? 单选按钮 ? 图片框 ? 选项卡控件 ? 滚动条 ? 进度条 ? ImageList 控件 ? ToolBar 控件 ? StatusBar 控件
? Timer 控件 ? TreeView 控件 ? ListView 控件

2.1.1 单选按钮(radioButton)

? Windows 窗体单选按钮控件以组的形式使用 ? 单选按钮允许用户从多个选项中选择一个选项
属性
Appearance Checked

说明
RadioButton 控件的显示与命令按钮相似 确定是否已选定控件

方法
Focus

说明
将输入焦点移至控件

2.1.1 单选按钮(radioButton)
如何按功能分组 Windows 窗体 RadioButton 控件?

1、在一个容器(如 Panel 控件、GroupBox 控件或窗体) 内绘制单选按钮即可将它们分组。 2、若要添加不同的组,必须将它们放到面板或分组框中。 步骤: 1、从“工具箱”的“Windows 窗体”选项卡中,将 GroupBox 或 Panel 控件拖到 窗体上。 2、在 GroupBox 或 Panel 控件上绘制 RadioButton 控件。 3、代码见下。

2.1.2 图片框
? 图片框控件表示可用于显示图像的 Windows 图片 框控件
属性
Image 用于指定图片框显示的图像。该图像可在设计或运行 时设置

图片框

说明

用于指定图像的显示方式。可以指定的各种大小模式 SizeMode 包括 AutoSize、 CenterImage、Normal 和 显示位图、元文件、图标、JPEG、GIF 是一种图形显示控件 StretchImage。默认值为 Normal 或 PNG 等格式的图形

方法
Show 显示控件

说明

2.1.2 图片框
? 练习1:使用设计器加载图片(Windows 窗体) ? 练习2:运行时候修改图片大小和位置
public Form1() { InitializeComponent(); showpic(); } public void showpic() { pictureBox1.Image = Image.FromFile(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + @"\image.gif"); pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; } private void pictureBox1_Click(object sender, EventArgs e) { if (pictureBox1.Image != null) { pictureBox1.Image.Dispose(); pictureBox1.Image = null; } else { showpic(); } }

2.1.3 选项卡控件
? 在 Windows 应用程序中,选项卡用于将相关的控 件集中在一起,放在一个页面中 ? 选项卡控件用于显示多个选项卡,其中每个选项卡 均可包含图片和其他控件 ? 选项卡相当于另一个窗体,可以容纳其他控件

选项卡控件

选项卡控件的属性
属性
MultiLine

说明
指定是否可以显示多行选项卡。如果可以显示多行选项卡, 该值应为 True,否则为 False。默认值为 False

当前所选选项卡页的索引值。该属性的值为当前所选选项 SelectedIndex 卡页的基于 0 的索引。默认值为 -1,如果未选定选项卡页, 则为同一值

SelectedTab
ShowToolTips TabCount

当前选定的选项卡页。如果未选定选项卡页,则值为 NULL 引用
指定在鼠标移至选项卡时,是否应显示该选项卡的工具提示。 如果对带 有工具提示的选项卡显示工具提示,该值应为 True,否则为 False 【同 时必须设置某页的ToolTipText内容 】

检索选项卡控件中选项卡的数目 更改 SelectedIndex 属性值时,将触发该事件

SelectedIndexChanged

选项卡控件的属性
属性
Alignment Appearance HotTrack Multiline RowCount

说明
控制标签在标签控件的什么位置显示。默认的位置为控件的顶部 控制标签的显示方式。标签可以显示为一般的按钮或带有平面样式 如果这个属性设置为true,则当鼠标指针滑过控件上的标签时,其外观就 会改变 如果这个属性设置为true,就可以有几行标签 返回当前显示的标签行数

SelectedIndex TabPages TabCount

返回或设置选中标签的索引 这是控件中的TabPage对象集合。使用这个集合可以添加和删除 TabPage对象 返回标签的总数

SelectedTab

返回或设置选中的标签。注意这个属性在TabPages的实例上使用

综合应用程序示例(一)
设定选项卡的数目

或者

综合应用程序示例(二)
设定选项卡的提示信息

分选项卡属性设计

总选项卡设计

综合应用程序示例(三)
统计页面操作基本信息

private void tabControl1_SelectedIndexChanged(object sender, EventArgs e) { label1.Text = "当前操作统计信息为:页面为第" + this.tabControl1.SelectedIndex.ToString() + "页,选项卡页为" + tabControl1.SelectedTab.Text + ",共有页数" + tabControl1.TabCount.ToString(); }

综合应用程序示例(四)
? ? ? ? 使用窗体接受职员的个人信息和职业信息 将使用单选按钮、图片框和选项卡控件 应用程序提供有两个选项卡页 第一个选项卡页显示个人信息的文本框

综合应用程序示例(四)
? 第二个选项卡页显示职员信息的文本框

综合应用程序示例(五)
? 选项卡的基本操作

2.1.4 进度条
? 用于指示操作的进度、完成的百分比 ? 外观是排列在水平条中的一定数目的矩 形

进度条的属性和方法
属性 Maximum 说明

进度条控件的最大值。默认值为 100 进度条控件的最小值。进度条从最小值开始递增, 直至达到最大值。默认值为 0 PerformStep 方法应据以增加进度条的光标位置 的值。 默认值为 10

Minimum
Step

Value
方法 Increment

进度条控件中光标的当前位置。默认值为 0
说明 按指定的递增值移动进度条的光标位置

PerformStep 按 Step 属性中指定的值移动进度条的光标位置
案例见下:

? 进度条练习1:加载数据库练习

private void button1_Click(object sender, EventArgs e) { string sqlstring = "Data Source=(local);Initial Catalog=zrzx;User ID=sa"; SqlConnection conn = new SqlConnection(sqlstring); string sql="select * from tbl_advice"; SqlCommand cmd = new SqlCommand(sql, conn); SqlDataAdapter adp = new SqlDataAdapter(); adp.SelectCommand = cmd; DataSet ds = new DataSet(); adp.Fill(ds); conn.Dispose(); conn.Close(); conn = null;
label1.Visible = true; progressBar1.Visible = true; progressBar1.Minimum = 0; progressBar1.Maximum = ds.Tables[0].Rows.Count; progressBar1.BackColor = Color.Red; for (int i = 0; i < ds.Tables[0].Rows.Count; i++) { progressBar1.Value++; Application.DoEvents(); this.label1.Text = progressBar1.Value.ToString(); } }

2.1.6 ImageList 控件
位于 Systems.Windows.Forms 命名空间内 在 ImageList 控件中添加图像的代码
ImageList 控件的属性和方法 imgImageList1.Images.Add (Image.FromFile("picture.gif"));

this.picMyPicture.Image = this.imgImageList1.Images[0]; 该属性表示图像列表中包含的图像的集 Images 合 Image 类的 FromFile() 方法用于手动将图像文件添加到 该属性表示图像的大小,默认高度和宽 ImageList 控件中 ImageSize 度为 16 x 16,最大大小为 256 x 256

属性

说明

方法
Draw

说明
该方法用于绘制指定图像

2.1.6 ImageList 控件
? 练习

第一步:加载imagelist控件 注意配置imagesize属性

第二步:配置images属性,添加图片信息

第三步:添加一个toolbar控件

第四步:指定 toolbar的imagelist 控件对象,并添加 具体的button对象。

2.1.7 ToolBar 控件
ToolBarButton ToolBar 类的属性和事件 类的属性和事件

属性
ImageIndex Buttons

两个类

说明

为工具栏按钮指定的图像在图像列表中的索引值 ToolBar ToolBarButton 工具栏按钮控件的集合

Parent
ShowToolTips Style ToolTipText ButtonClick

事件

指定工具栏按钮所属的 ToolBar 控件 鼠标移到各工具栏按钮上时,是否显示相应的工 工具栏按钮的样式,其中包括 具提示,如果该属性的值设置为 True,则显示 DropDownButton(下拉按钮)、Separator 工具提示 (分隔符) 和 ToggleButton(切换按钮)
表示工具栏按钮的工具提示文本 单击工具栏按钮时,将触发该事件 工具栏

说明

工具栏 – 示例
输出屏幕 添加了三个按钮

? 创建名为 Example 1 的 Windows 应用程序。 处理 ButtonClick 事件的代码 ? 将 Form1.cs 更改为 frmToolBarExample.cs。
private void tbrToolBar_ButtonClick(object sender, ? 将 ToolBar 控件拖动到窗体上,并将它命名为 System.Windows.Forms.ToolBarButtonClickEventArgs e) { tbrToolBar。 if(e.Button == this.tbrToolBar.Buttons[0]) { ? 选择 Buttons 属性,并单击按钮以显示“ToolBarButton MessageBox.Show(“已单击 “打开”按钮"); 集合编辑器”窗口。 } }

? 添加三个按钮。

案例
第一步:添加toolbar控件
第二步:设置toolbar属性

第三步:添加5个button

第四步:添加imagelist控件
private void Form2_Load(object sender, EventArgs e) { toolBar1.ImageList = imageList;

toolBarButton1.ImageIndex = 0; toolBarButton2.ImageIndex = 1; toolBarButton3.ImageIndex = 2; toolBarButton4.ImageIndex = 3; toolBarButton5.ImageIndex = 4;
}

第五步:配置imagelist的images属性

2.1.8 statusStrip 控件
StatusStripStatusLabel使用文本和图像向用户显示应用程序 当前状态的信息。

案例:统计文本字数信息【代码见下】

2.1.8 statusStrip 控件
案例:c# winForm 将窗体状态栏StatusStrip 分成左 中右三部分 右边显示当前时间 通过StatusStrip显示窗体状态栏 同时将状态栏分成三部分 居左边显示相关文字信息 中间空白显示 居右边显示时间信息 【具体实现见下】

2.1.8 statusStrip 控件
案例:c# StatusStrip 通过什么方式显示进度?

2.1.9 Timer 控件简介
03:15:04
在应用程序中显示实际时间

WinForms 的 Timer 控件
按指定的时间长度显示图像

需要一种可在程序运行时操控时间的机制

2.1.9 Timer 控件简介
拖放

2.1.9 Timer 控件简介
对特定的时刻响应 Timer 类包含在 System.Windows.Forms 命名空间中

按照某个周期触发事件 与用户无关,可以通 过编程,在规定的时 刻执行相应动作

组件与其他控件不同,它不向用户 提供用户界面,因此没有必要显示 在 WinForms 设计器的界面上

2.1.9 Timer 控件的必要性

Timer 控件为开发人员提供了一种在经过指定的时间间隔或到达指定 的绝对时间时根据代码进行响应的方式

2.1.9 Timer 控件的属性、方法和事件
属性
Enabled Interval时间间隔毫秒数 事件

方法
Start() 时钟启动 Stop() 时钟停止

Tick 每隔Interval时间间隔触发 一次

Timer 控件名的前缀是 "tmr“

例如:tmrTicker, tmrTickTimer

Timer 控件应用程序示例1
要求显示计算机运行的时间长度和应用程序运行的时间长度

Timer 控件应用程序示例1
动画演示:移动的画面
private void button1_Click(object sender, EventArgs e) { timer1.Start(); } private void button2_Click(object sender, EventArgs e) { timer1.Stop(); }
private void timer1_Tick(object sender, EventArgs e) { pictureBox1.Left -= 5; if (pictureBox1.Right < 0) { pictureBox1.Left = Width; } }

Timer 控件应用程序示例2
public class frmTickCounter : System.Windows.Forms.Form { private int compuTime; ………………….. 为窗体声明一个私有整型变量

private void frmTickCounter_Load(object sender, System.EventArgs e) { compuTime = Environment.TickCount; }
frmTickCounter 窗体的 Load 事件

Timer 控件应用程序示例2
private void tmrTickTimer_Tick(object sender, System.EventArgs e) 应用程序中使用的局部变量 { long curTickValue = Environment.TickCount; long difference = curTickValue - compuTime; long computerHours, computerMinutes, computerSeconds; long applicationHours, applicationMinutes, applicationSeconds; //将毫秒转换成小时、分钟和秒 computerHours = (curTickValue / (3600 * 999)) % 24; computerHours = (curTickValue / (3600 * 999)) % 24; computerMinutes = (curTickValue / (60 * 999)) % 60; computerSeconds = (curTickValue / 999) % 60; applicationHours = (difference / (3600 * 999)) % 24; applicationMinutes = (difference / (60 * 999)) % 60; applicationSeconds = (difference / 999) % 60; 将毫秒转换成小时、分钟和秒

Timer 控件应用程序示例2
显示每个标签的输出结果 this.lblComputer.Text = String.Format(“这台计算机已经开机 {0} 小 时, {1} 分钟 {2} 秒",computerHours.ToString(),computerMinutes. ToString(),computerSeconds.ToString()); this.lblApplication.Text = String.Format(“这个程序已经运行了 {0} 小 时, {1} 分钟 {2} 秒", applicationHours.ToString(), applicationMinutes.ToString(),applicationSeconds.ToString()); }

private void btnClose_Click(object sender,System.EventArgs e) { this.Close(); 关闭应用程序 }

Timer 控件应用程序示例3
流失的时间:

1.用.net建立一个Windows应用程序 2.在项目下添加一个类命名为“Time“ 3.编写“Time“类得实现代码 4.在窗体中放两个Label控件和一个时钟(Timer)控件 5.编写窗体得Load事件和(Timer)控件的Tick事件 【代码见下】

2.1.10 ListView 控件
选择列出项目的视图类型

可折叠的 TreeView

ListView

2.1.10 ListView 控件
创建与 Windows 资源管理器 的右窗口相似的用户界面 可用于以特定样式或视 图类型显示列表项

四种视图模式,即大图标、小图标、列表和详细资料

ListView 控件的属性和方法
ListView 控件名的前缀是 “lvw“
如:lvwList, lvwListItems

属性
Items MultiSelect SelectedItems Sorting column View ListView中的具体内容 允许选择多个项 用户选择的listview行 指定进行排序的方式 详细视图中显示的列信息 选择五种视图中的一种 BeginUpdate Clear() GetItemAt() Sort()

方法
徹度清除視圖,刪除所有的 選項和列 返回列表視圖中位於 x,y 的 選項 開始更新,直到調用 EmdUpdate為止。當一次 插入多個選項使用這個方法 很有用,因為它會禁止視圖 閃爍,並可以大大提高速度 結束更新

EndUpdate

Column 集合和 Column 对象
? ListView 控件的 Columns 属性表示控件中出 现的所有列标题的集合 ? 列标题是 ListView 控件中包含标题文本的一 个项 ? ColumnHeader 对象定义在控件的 View 属性 设置为“Details‖值时,作为 ListView 控件的 一部分显示的那些列 ? 如果 ListView 控件没有没有任何列标题,并 且 View 属性设置为 Details,则 ListView 控 件不显示任何项

ListView 控件的编辑列,组和项
案例1、编辑列

ListView 控件的编辑列,组和项
案例1、编辑列

ListView 控件的编辑列,组和项
案例1、编辑列

项集合和项对象
? ListView 控件的 Items 属性表示包含控件 中所有项的集合 ? 该属性返回 ListView.ListViewItemCollection,可以用于 ListView 中添加新项、删除项或计算可用项 数

案例2、添加删除记录
private void Form2_Load(object sender, EventArgs e) { ColumnHeader objheader = new ColumnHeader(); objheader.Text = "姓名"; objheader.TextAlign = HorizontalAlignment.Center; objheader.Width = 40; listView1.Columns.Add(objheader); } private void button1_Click(object sender, EventArgs e) { listView1.Columns.Add("年龄", 40, HorizontalAlignment.Center); listView1.Columns.Add("班级", 40, HorizontalAlignment.Center); listView1.Columns.Add("性别", 40, HorizontalAlignment.Center); listView1.Columns.Add("职业", 40, HorizontalAlignment.Center); button1.Enabled = false;

} private void button2_Click(object sender, EventArgs e) { listView1.Columns.Remove(listView1.Columns[1]); }

案例3:

案例见下:

如何各行显示不同 颜色?

for (int i = 0; i < this.listViewControl.Items.Count; i++) { if (i % 2 == 0) { this.listViewControl.Items[i].BackColor = Color.Gray; } }

案例4:清除列、全部、显示某行信息

通过代码生成见下
private void button4_Click(object sender, EventArgs e) { this.listView1.Clear(); }

private void button5_Click(object sender, EventArgs e) { listView1.Items.Clear(); }
private void listView1_SelectedIndexChanged(object sender, EventArgs e) { foreach (ListViewItem lst in listView1.SelectedItems) { //在这里对lst进行处理,lst代表每一个被选中的ListViewItem MessageBox.Show(lst.Text);//这里示例显示每一个被选中的项的第一列的文本 }

}

案例5:从数据库之中读取数据,并在listview之中显示

2.1.11 TreeView 控件

2.1.12 TreeView 控件
用于以节点形式显示文本或数据, 这些节点按层次结构顺序排列 在 TreeView 控件中,复选框 和图标可以和文本一起显示

Windows 资源管理器中左窗格所包含的 目录和文件是以树型视图样式排列的

节点集和节点对象
? TreeView 控件的 Nodes 属性表示为 TreeView 控件指定的树节点集 ? 树节点集中的每个树节点对象可包括它本 身的树节点集 ? 树节点集中 Add()、Remove() 和 RemoveAt() 方法使开发人员可添加和移动 集中的单个树节点

添加、修改和删除节点
? TreeView 控件以层次结构 方式显示节点 ? 在将新节点添加到现有 TreeView 时,重要的是注 意新节点所添加到的父节 点

? 可用设计器模式或用户界 面在 TreeView 上添加或删 除节点

添加、修改和删除节点
在窗体上选择已存在的 TreeView 控件或在窗体上添加一个新控件

要使用树节点编辑器删除节点,请重复步骤 1 和 2。调用 树节点编辑器,然后选择要删除的节点,单击“删除” 在“属性”窗口中单击节点属性旁的省略号 (…) 按钮,调用树节点编 辑器

添加到树的第一个节点是根节点, 其他节点可添加到存在根节点的树上

通过选择根节点或任何其他节点, 然后单击“添加子级”按钮,可为树添加子节点

添加、修改和删除节点
可通过编程方式向 TreeView 添加节点 ………… TreeNode chNode = new TreeNode("Text for new node"); tvwTree1.Nodes.Add (chNode); ………… 可通过编程方式从 TreeView 删除节点 …………. tvwTree1.Nodes.Remove(tvwTree1.currNode); // 清除所有节点 tvwTree1.Nodes.Clear(); ……………

如果没有选定要删除的节点,则会删除根节点

TreeView 控件的事件
事件
AfterCheck AfterCollapse AfterExpand AfterSelect BeforeCheck BeforeCollapse BeforeExpand BeforeSelect
TreeView 控件名的前缀是 "tvw" 如:tvwTree, tvwList

TreeView 案例1
1、加入子节点:

加入子节点的具体过程是:首先要在TreeView组件中定位要加入的子
节点的位置,然后创建一个节点对象,然后利用TreeVeiw类中对节点的 加入方法(即:Add ( )方法),加入此节点对象。 treeView1.SelectedNode.Nodes.Add ( tmp ) ;

2、加入兄弟节点:
treeView1.SelectedNode.Parent.Nodes.Add ( tmp ) ;

3、删除节点:
删除节点的具体过程是:首先判断要删除的节点是否存在下一级节点,如 果不存在,就调用TreeView类中的Remove ( )方法,就可以删除节点了。 treeView1.SelectedNode.Remove ( ) ;

TreeView 案例1
4、TreeView组件的一些其他常用操作 :
< I > 展开所有节点: //定位根节点 treeView1.SelectedNode = treeView1.Nodes [ 0 ] ; //展开组件中的所有节点 treeView1.SelectedNode.ExpandAll ( ) ; < 2 >展开选定节点的下一级节点 : treeView1.SelectedNode.Expand ( ) ;

< 3 >折叠所有节点 : //定位根节点 treeView1.SelectedNode = treeView1.Nodes [ 0 ] ; //折叠组件中所有节点 treeView1.SelectedNode.Collapse ( ) ;

TreeView 案例1
5、LISTVIEW实际案例 :

代码见下:

2.1.13
1、属性及定义 Items MutiColumn

CheckedListBox 可选列表框控件
描述控件对象中的所有项。 决定是否可以以多列的形式显示各项。在控件对象的指定高度 内无法完全显示所有项时可以分为多列,这种情况下若 MutiColumn属性值为false,则会在控件对象内出现滚动条。 当控件对象支持多列时,指定各列所占的宽度 决定是否在第一次单击某复选框时即改变其状态 指示复选框列表控件的可选择性。该属性只有两个可用的值 None和One,其中None值表示复选框列表中的所有选项都处于 不可选状态;One值则表示复选框列表中的所有选项均可选。 表示控件对象中的各项是否按字母的顺序排序显示 表示控件对象中选中项的集合,该属性是只读的

ColumnWidth CheckOnClick SelectionMode

Sorted CheckedItems

CheckedIndices 表示控件对象中选中索引的集合

2.1.13
2、常用的方法

CheckedListBox 可选列表框控件
设置列表中的某个复选框的选中状态。
设置列表中的某个复选框的待选状态。

SetItemChecked
SetSelected

2.1.13

CheckedListBox 可选列表框控件

可选列表框控件CheckedListBox类似于ListBox,但是其列表项的左侧含可以 显示选择框。

2.1.14

numericUpDown微调按钮控件

看起来像文本框和一组箭头的组合,用户可以通过单击箭头来调整具体的 数值。用户可以通过单击向上和向下的箭头按钮,增大或减小参数值。 具体属性见下表所示:

Increment Maximum Minmum Updownalign InterceptArrowKeys

递增量,默认为1。 最大值,默认100。 最小值,默认0. 设置微调按钮的位置,Left或者Right 是否接受上下箭头的控制。

2.1.14

numericUpDown 微调按钮控件

案例:控制图片的移动

2.1.15 monthCalendar日历控件
Windows 窗体 MonthCalendar 控件为用户查看和设置日期信息提供了一 个直观的图形界面。该控件以网格形式显示日历: 网格包含月份的编号日期,这些日期排列在周一到周日下的七个列中, 并且突出显示选定的日期范围。可以单击月份标题任何一侧的箭头按钮来 选择不同的月份。与类似的 DateTimePicker 控件不同,您可以使用该控 件选择多个日期。 monthCalendar控件通常用于选择日期,典型的日历控件如下图:

2.1.15 monthCalendar日历控件
2、基本属性 backcolor
SelectionRange Minmum Showtody, showtodaycircle, showweeknumbers Titlebackcolor TitleForcolor trailingcolor

月份中显示背景色。
在月历中显示的起始时间范围,Begin为开始,end为截至。 最小值,默认0. 是否显示今天日期 是否在今天日期上加红圈 是否左侧显示周数(1-52周) 日历标题背景色。 日历标题前景色 上下月颜色

2.1.15 monthCalendar日历控件
3、实验案例

页面布局

运行样式

2.1.17 DataTimePicker控件
如果希望应用程序能够使用户可以选择日期和时间,并以指定的格式显示 该日期和时间,可以使用 DataTimePicker控件。 DataTimePicker控件用于选择日期和时间,与Monthcalendar控件不同, DataTimePicker控件只能够选择一个时间段。一个基本的 DataTimePicker控件如下所示:

2.1.17 DataTimePicker控件
1、属性 showcheckbox checked showupdown value 2、案例 是否在控件中显示复选框,当复选框为选中时候,表示未 选择任何值。 当showcheckbox 为TRUE时候,确定是否选择复选框。 改为数字显示框,不再显示月历表。 当前的日期(年月日时分秒)

2.1.18 为程序添加多媒体功能

2.1.18 为程序添加多媒体功能

2.1.19 用户自定义控件
除了使用VS2005提供的与定义控件之外,C#还允许用户进行控件的自行定义。 使用.NET FormWork可以开始和实现一些新的控件,而所有的控件无论是系统自带 的抑或是用户自定义的,都来自control类。此处先对control类进行介绍。 control类是windows窗体控件的基类,提供了windows窗体控件中进行可视化显示 所需的所有基础结构。 由于control类提供了很多基础结构,使得开发用户自定义的控件变得相对简单。 通常情况下,开发人员自行编写的控件可以分为三类: ? 复合控件:组合现有的控件实现功能; ? 扩展控件;扩展基本控件的功能; ? 自定义控件;从头开始创建一个全新的控件

在下列情况下,可以编写自定义控件: 1、想要提供控件的自定义图形化表示形式; 2、需要实现无法从标准控件获取的自定义功能;

2.1.19 用户自定义控件
简单案例:

编译 给自定义的控件添加代码

本节小结
? WinForms单选按钮控件允许用户进行设置 ? WinForms的图片框控件允许用户在窗体上添加和 显示位图、元文件、JPEG、GIF 或 PNG 等格式的 图形 ? WinForms的选项卡控件将类似的功能集中在一起, 放在一个对话框或窗口中 ? 进度条控件用于指示操作的进度,并显示排列在 水平条中一定数目的矩形,通常通过在程序中设 置其Value值来显示任务完成的百分比

本节小结
? Timer 控件为开发人员提供了一种在指定时刻或指定的 周期执行任务的控件 ? Timer 控件的 Interval 属性表示时钟的周期,单位为毫 秒 ? ListView 控件用于以特定样式或视图类型显示列表项, 其Items集合对象提供了对其列表项的操作 ? TreeView 控件用于以节点形式显示文本或数据,这些 节点按层次结构顺序排列 ? TreeView控件的Nodes集合对象提供了对树型节点的操 作

2.2 单(多)文档操作及菜单
本节学习目标:

? ? ? ? ?

了解 MDI 应用程序和 SDI 应用程序 了解菜单和掌握菜单控件的使用 掌握 ImageList 控件 掌握 ToolBar 控件和 StatusBar 控件 演示应用程序示例

2.2.1 单文档和多文档
这是 Ann 的一个测试

如已有一个文本文件打 开,在同一记事本应用 新建一个记事本实例来 程序中,不允许创建第 打开第二个文本文件 二个文本文件

在SDI应用程序中一次只能打开一个文件

2.2.1 单文档和多文档
应用程序窗口 应用程序窗口
同时打开多个文件

在 MDI 应用程序中可以同时打开多个文件

文档窗口 文档窗口

2.2.2 主窗体和子窗体
父窗体 子窗体 子窗体的菜单并入父窗体中

2.2.2 主窗体和子窗体 3-2
主窗体的特点: ? 启动一个 MDI 应用程序时,首先显示父窗体 ? 它是应用程序中所有其他窗口的容器 ? 每个应用程序界面都只能有一个 MDI 父窗体 ? 在任何指定的时间都可以打开多个子窗体 ? 任何 MDI 子窗体都不能移出 MDI 框架区域 ? 关闭 MDI 父窗体则自动关闭所有打开的 MDI 子窗体

2.2.2 主窗体和子窗体
MDI 应用程序的属性、方法和事件

事件 属性
Closed MdiChildren Closing
MdiParent MdiChildActivate ActiveMdiChild

由用户或窗体的 Close 方法关闭窗体后, 用于获取表示多文档界面 (MDI) 子窗体 发生该事件 的窗体数组 正在关闭窗体时,发生该事件 用于获取或设置当前多文档界面 (MDI) 在 MDI 应用程序中激活或关闭多 父窗体 (MDI) 子窗体时,触发该事件 文档界面 用于获取当前活动的多文档界面 (MDI) 子窗体

说明 说明

方法 说明 如果窗体为 MDI 父窗体,则在触发 MDI 父窗体的 Closing 事件之前,将触发所有 MDI 子窗体的 Closing 事件。另外,在触发 MDI 父窗体的 Closed 事件之前,将 ActivateMdiChild 用于激活子窗体 触发所有 MDI 子窗体的 Closed 事件
LayoutMdi 排列 MDI 父窗体中的多文档界面 (MDI) 子窗体

2.2.3 创建 MDI 窗体
创建 MDI 窗体步骤:

? 将 IsMdiContainer 属性设置为 True
? 选择“项目”?“添加 Windows 窗体”
Closing 事件的 ? 添加给定的代码,将 调用顺序: TestForm 设置为子窗体

TestForm objChild = new TestForm(); objChild.MdiParent = this; MDI 子窗体 MDI 父窗体 objChild.Show();

2.2.4 激活和去激活窗口
激活窗口 活动窗口 this.ActivateMdiChild(frmChild); 显示活动窗口

显示活动窗口的名称 MessageBox.Show(Convert.ToString(this.ActiveMdiChild));

2.2.5 排列子窗口
MDILayout 枚举的成员

成员名称
ArrangeIcons

说明
在 MDI 父窗体的客户端区内排列所有 MDI 子窗体 的图标 在 MDI 父窗体的客户端区内层叠所有 MDI 子窗口
在 MDI 父窗体的客户端区内水平平铺所有 MDI 子 窗口 在 MDI 父窗体的客户端区内垂直平铺所有 MDI 子 窗口

Cascade
TileHorizontal TileVertical

2.2.5 窗口MDI&SDI案例1 ——菜单和窗口设计技术
第一步:设计窗体界面

(1)配置主窗体为容器属性

(2)设置系统菜单和右键菜单

(3)添加菜单项文字

2.2.5 窗口MDI&SDI案例1 ——菜单和窗口设计技术
第二步:代码设计

private void ChildMenuItem1_Click(object sender, EventArgs e) { Form5 childform1 = new Form5(); childform1.MdiParent = this; childform1.Show(); }
private void ChildMenuItem2_Click(object sender, EventArgs e) { Form6 childform2 = new Form6(); childform2.MdiParent = this; childform2.Show(); } 菜单显示窗口

2.2.5 窗口MDI&SDI案例1 ——菜单和窗口设计技术
第二步:代码设计 (右键操作功能)
private void RightMenuItem1_Click(object sender, EventArgs e) { Form mynewform = (Form)this.ActiveMdiChild; MessageBox.Show("焦点窗口是:"+Convert.ToString( mynewform.Text)); }

private void RightMenuItem2_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.Cascade); }
private void RightMenuItem3_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.TileHorizontal); } private void RightMenuItem4_Click(object sender, EventArgs e) { this.LayoutMdi(MdiLayout.TileVertical); }

2.2.6 菜单和菜单组件
1、菜单简介

菜单是程序中显示一个选项列表的图形元素 菜单提供了将命令分组 的一致方法
主菜单

用户易于访问 一个菜单可以带有若干 子菜单 支持使用访问键启用键 盘快捷方式

这表明其附有子菜单

子菜单

2.2.6 菜单和菜单组件
2、建立简单的菜单 (1)建立winform窗体并添加菜单组件

(2)注意菜单命名时候的问题;避免汉字出现。

2.2.6 菜单和菜单组件(案例)
(3)快捷键问题

创建主菜单

配置快捷键

如果在命名时候在text属性处键入“文件(&F)”,将会产生“文件(F)”的 效果, &将被认为是快捷键的字符。运行时候为alt+f键执行。同理,菜单功能在分组时 候可以加入分隔符号:

在初始化事件中,进行菜单的动态添加。(代码见下)

控件

属性
Name: frmMdiApplication Text: 图书管理系统 Menu: mnuBookManage IsMdiContainer: True Name: mnuBookManage Name: mnuEnterStore

应用程序示例

Form

由菜单、工具栏和状态栏组成的应用程序
Menu MenuItem MenuItem MenuItem

设置窗体上控件的属性 ? 创建名为 BookManagement的 Windows 应用程序。 Text: 新书入库 (&N)

? 将名称 Form1.cs 更改为 frmMdiApplication.cs。

Name: mnuSearchBook Text: 查询书目(&S) Name: mnuConManage Text: 菜单管理(&M)

? 将 Menu、StatusBar 、ToolBar和 ImageList 控件拖动 MenuItemName: mnuExitText: MenuItem 到窗体上。 系统(&E)
MenuItem MenuItem MenuItem ToolBar
Name:mnuEnginerrBook Text:工程类图书 Name:mnuForeignBook Text:外文类图书 Name:mnuChineseBook Text:中文类图书 Name: tbrBookBar Name: sbrBookStatus

退出

演示:创建 Windows 应用程序示例的步骤 StatusBar

应用程序示例

? 选择 ToolBar 控件,单击 ToolBar 控件的 Buttons 属性,此时会出现“ToolBarButton 集合编辑器” 窗口,向其中添加3个按钮,并分别命名为 trbNewBooks 、trbBookSearch和trbExit。分别 在Text属性处输入入库、查询和退出

单击“确定”以继续

演示:创建 Windows 应用程序示例的步骤

应用程序示例
在 将以下代码添加到 mnuEnginerrBook mnuConManage frmMdiForm 菜单项的 Click 的“ Load 菜单管理”的 事件中添加代码 事件中 Click 事件中 ? 将以下代码添加到 选择 StatusBar 控件, 并将 void ShowPanels 属性 private frmMdiForm_Load(object mnuConManage_Click(object mnuEnginerrBook_Click(object sender, sender, sender, System.EventArgs System.EventArgs System.EventArgs e) e) e) { 设置为 True ContextMenu frmNewBooks mnuContextMenu newBooks=new frmNewBooks(); == new ContextMenu(); this.sbrBookStatus.Panels[0].Text ? 选择 Panels 属性,并添 DateTime.Now.ToShortDateString(); this.ContextMenu newBooks.MdiParent=this; = mnuContextMenu; 加两个面板,命名为 } sbrTimeBar newBooks.WindowState=FormWindowState.Maximized; mnuContextMenu.MenuItems.Add(" 新书入库"); 和 newBooks.Show(); mnuContextMenu.MenuItems.Add(" 查询书目"); sbrNameBar
} } this.sbrBookStatus.Panels[1].Text=this.ActiveMdiChild. mnuContextMenu.MenuItems.Add(" 退出系统"); Text.ToString();

单击“确定”以继续 创建新窗体,命名为frmNewBooks.cs,当鼠标单击“工程类图书” 菜单选项时弹出此窗体

本节小结
? 单文档界面的某一时刻只能打开一个文档, 多文档界面允许同时打开多个文档 ? MDI应用程序由一个MDI父窗体和一个或多 个子窗体构成 ? 菜单是程序中显示一个选项列表的图型元 素,它提供将命令分组的方法和用户对其 访问的简单途径

本节小结
? 上下文菜单用于使用户通过单击鼠标右键 访问常用的命令 ? ImageList 控件是一种图形存储控件,可以 包含单个图像或图像集合 ? 工具栏包含工具栏按钮,这些按钮提供对 应用程序中最常用的菜单命令的快速访问 ? 状态栏通常显示在窗体的底部,向用户提 供有关应用程序状态的信息

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

第三章、文件处理

CONTENT
?本章主要内容介绍
3.1 3.2 3.3 windows和窗体 Visual Stutio .net IDE简介 事件处理

本章学习目标:

? ? ? ?

了解System.IO 命名空间 掌握读写文本文件的方法 掌握向文件读写二进制数据的方法 掌握读写内存流的方法

3.1 System.IO 命名空间
另存 为 .xls 文 件

另存 为 .bmp 文件

另存 为 .txt 文件 以字节形式向磁盘写数据通常称为字节流(比特流)。存储在磁 盘上的字节集合称为文件

3.1 System.IO 命名空间
类 密封类

抽象类

静态类

静态类

静态类

密封类 抽象类

密封类

3.1 System.IO 命名空间
1、System.io类介绍
File Directory
提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建 FileStream 对象。 公开用于创建、移动和枚举通过目录和子目录的静态方法。无法继承此类。

Path
Fileinfo Directoryinfo Filestream Streamreader filesyswatcher

对包含文件或目录路径信息的 String 实例执行操作。这些操作是以跨平台的方式 执行的。
提供创建、复制、删除、移动和打开文件的实例方法,并且帮助创建 FileStream 对象。无法继承此类。 公开用于创建、移动和枚举目录和子目录的实例方法。无法继承此类。 公开以文件为主的 Stream,既支持同步读写操作,也支持异步读写操作。 实现一个 TextReader,使其以一种特定的编码从字节流中读取字符。 侦听文件系统更改通知,并在目录或目录中的文件发生更改时引发事件。

3.1 System.IO 命名空间
2、File类的常用方法

静态方法
Move 将指定的文件移到移到指定位置,剪切 Delete 删除指定文件,如果文件不存在,则将引发异常 Copy 将现有的文件复制到新文件 CreateText 创建文件 OpenText 打开文件文本 Open 打开文件

3.1 System.IO 命名空间
试一试:

把C:\WinNT\Win.INI文件拷贝到C:\下的代码,怎么写?
using System.IO; …… private void button1_Click(object sender, EventArgs e) { string path = @"C:\WINDOWS\IE4 Error Log.txt"; string target = @"c:\1.txt"; if (!File.Exists(path)) { MessageBox.Show("对不起,未发现路径文件!"); } else { File.Copy(path,target); MessageBox.Show("复制成功!"); } }

3.1 System.IO 命名空间
功能改进:【代码见下】

3.1 System.IO 命名空间
3、Fileinfo类的常用方法

静态方法
Attributes
获取或设置当前 FileSystemInfo 的 FileAttributes

CreationTime
Directory

获取或设置当前 FileSystemInfo 对象的创建时间。

获取父目录的实例 获取表示目录的完整路径的字符串。

DirectoryName

Exists

获取指示文件是否存在的值

Extension 获取表示文件扩展名部分的字符串

3.1 System.IO 命名空间
更改代码:

此次我们通过更换FileInfo类执行同样的动作;
using System.IO; …… private void button1_Click(object sender, EventArgs e) { string path = @"C:\WINDOWS\IE4 Error Log.txt"; string target = @"c:\1.txt"; FileInfo myfile = new FileInfo(path); if (!myfile.Exists) { MessageBox.Show("对不起,未发现路径文件!"); } else { myfile.CopyTo(target); MessageBox.Show("复制成功!"); }

3.1 System.IO 命名空间
案例练习:显示文件的基本信息:【案例见下】

3.1 System.IO 命名空间
? FileInfo类和File类
– 两者都提供对文件类似的操作
– File为静态类,直接使用;FileInfo需要实例化后才能 使用 。

– 从性能上考虑,如果你要多次操作文件,不管是针对 相同的,还是不同的,请使用FileInfo,说白了,单打 独斗File最棒,群殴则首推FileInfo。 – 每次通过File类调用某个方法时,都要占用一定的cpu,
而FileInfo类只在创建FileInfo对象时执行一次安全检查。

3.1 System.IO 命名空间
4、文件夹类Directory的常用方法

静态方法
Move 将文件或目录及其内容移到新位置 CreateDirectory 创建指定路径中的所有目录

Delete
Exists

删除指定的目录。
获取父目录的实例

GetCreationTime 获取目录的创建日期和时间 。 GetCurrentDirectory 获取应用程序的当前工作目录 GetFiles 返回指定目录中的文件的名称

3.1 System.IO 命名空间
4、文件夹基本操作实验【代码见下】

3.1 System.IO 命名空间
5、 File类的常用操作的静态方法 System.IO 命名空间 File 类

静态方法
CreateText(string FilePath)

继承类

OpenText(string FilePath) Open(string FilePath, FileMode) Create(string FilePath) OpenRead(string FilePath) AppendText(string FilePath)

FileStream 类

3.1 System.IO 命名空间
5、 File类的常用操作的静态方法练习

3.2 文件流类Filestream
FileStream实例用于读写文件中的数据。要构造FileStream实例, 需要以下4条信息: ?要访问的文件。 ?表示如何打开文件的模式。例如,创建一个新文件或打开一个 现有的文件。如果打开一个现有的文件,写入操作是覆盖文件原 来的内容,还是添加到文件的末尾? ?表示访问文件的方式—— 是只读、只写,还是读写? ?共享访问——表示是否独占访问文件。如果允许其他流同时访 问文件,则这些流是只读、只写,还是读写文件?

3.2 文件流类Filestream
1、filestream的构造函数
FileStream 已重写构造函数
FileStream(string FilePath, FileMode) FileStream(string FilePath, FileMode, FileAccess) FileStream(string FilePath, FileMode, FileAccess, FileShare)

在构造函数中使用的 FilePath, FileMode, FileAccess, FileShare 分别是指:使用指定的路径、创建模式、读/写权限和共享权限

3.2 文件流类Filestream
1、filestream的构造函数 参数
FileMode
FileAccess FileShare Read、ReadWrite和Write Inheritable、None、Read、ReadWrite和Write


Append、Create、CreateNew、Open、OpenOrCreate和 Truncate

注意,对于FileMode,如果要求的模式与文件的现有状态不一致,就会抛出一 个异常。如果文件不存在,Append、Open和Truncate会抛出一个异常,如果 文件存在,CreateNew会抛出一个异常。Create和OpenOrCreate可以处理这 两种情况,但Create会删除现有的文件,创建一个新的空文件。FileAccess 和 FileShare枚举是按位标志,所以这些值可以与C#的按位OR运算符|合并使用。

1、filestream的构造函数

FileMode 和FileShare,FileAccess
? FileMode
? FileShare ? FileAccess
– Write – ReadWrite
………… – Read – None – Append FileStream fstream = new FileStream("Test.cs", FileMode.OpenOrCreate,

– Read – Create FileAccess.ReadWrite, FileShare.None); ……… – Write – CreateNew – Open – OpenOrCreate – Truncate

– ReadWrite

FilePath:将封装的文件的相对路径或绝对路径

3.3 文件读写例子 4-1
——文件流filestream综合案例
第一步:建立CS文件,并准备填写关于文件操作的各种方法

具体代码见下(1) 第二步:建立窗体文件,样式如下图所示 具体代码见下(2)

3.3 文件读写例子 4-2
——文件流filestream综合案例【随堂练习】
【本案例您将学习到】 ? ? ? ? ? ? 如何通过用户选择文件夹,获取文件 夹信息 如何通过用户选择文件,获取文件信 息。 如何通过文件流建立一个新的文本文 件。 如何打开文本文件后重新写文本信息 流 如何在C#中定义文件和文件夹? 文件流的资源释放意义以及释放资源 的基本顺序。

3.3 文件读写例子 4-3
——文件流filestream综合案例【随堂练习】
【本案例您将学习到】 ? ? ?

充分了解和学习文件对话框机 制 文本文件的文件夹,文件管理。 文本文件综合操作:复杂编辑, 综合查询,删除,插入等操作。

3.4 读写二进制文件
最后研究的读取器/编写器组是BinaryReader和BinaryWriter,它们都从 System.Object直接派生。这些类型可以让我们从基层流中以简洁的二进 制格式读取或写入离散数据类型。BinaryWriter类型定义了一个多次重载 的Write()方法,用于把数据类型写入基层的流。除了Write()方法, BinaryWriter还提供了另外一些成员让我们能获取或设置从Stream派生的 类型,并且提供了随机数据访问的支持。

? 要使用 BinaryReader 和 BinaryWriter 类 ? 这两个对象都需要在FileStream上创建

FileStream filestream = new FileStream(Filename, FileMode.Create); BinaryWriter objBinaryWriter = new BinaryWriter(filestream);

二进制文件读写对象
BinaryReader Close() Read() ReadDecimal() ReadByte() ReadInt16() ReadInt32() ReadString() BinaryWriter Close() Flush() Write()

写二进制文件案例(1)——图片的存储与复制
运行后效果

【本案例您将学习到】 ? ? 运行前效果 图片文件二进制流的读取并显示在图像控件 之中; 如何将图像控件图像读取到内存流,并另外 存储。 saveFileDialog控件在另存文件中的作用。

?

写二进制文件案例(2)—— 图片文件在数据库之中的存储与读取

运行后效果

【本案例您将学习到】
? ? ? 图片类型数据如何存储在数据库之中; 如何从数据库的图片数据中读出数据并 显示图片信息。 二进制流和内存流在图片处理过程之中 的联合使用。

运行前效果

3.5 读写内存流
Stream 类
抽象类

MemoryStream

BufferedStream

1. 对内存而不是对磁盘进行数据读写 1. 对缓冲区进行数据读写 2. 减少了对临时缓冲区和文件的需要 2. 允许操作系统创建自己的缓冲区 3. 输入/输出效率高且速度更快 4. 在网络通讯的时候经常会使用到

1、 读写内存流 ——MemoryStream 类
MemoryStream 类创建这样的流,该流以内存而不是磁盘或网络连接作为 支持存储区。MemoryStream 封装以无符号字节数组形式存储的数据,该 数组在创建 MemoryStream 对象时被初始化,或者该数组可创建为空数组。 可在内存中直接访问这些封装的数据。内存流可降低应用程序中对临时缓 冲区和临时文件的需要。

FileStream对象与MemoryStream对象的区别:
FileStream对象的数据来自文件,而MemoryStream对象的数据来自内存缓 冲区。这两个类都继承自Stream类。 MemoryStream的数据来自内存中的一块连续区域,这块区域称为“缓冲区 (Buffer)”。可以把缓冲区看成一个数组,每个数组元素可以存放一个字节的 数据。 在创建MemoryStream对象时,可以指定缓冲区的大小,并且可以在需要的 时候更改。

1、 读写内存流 ——MemoryStream 类
其基本的构造函数如下: 名称 MemoryStream () MemoryStream (byte[]) MemoryStream (byte[], Boolean) 说明 使用初始化为零的可扩展容量初始化 MemoryStream 类的新实例。 基于指定的字节数组初始化 MemoryStream 类的无法调整大小 的新实例。 使用按指定要求设置的 CanWrite 属 性基于指定的字节数组初始化 MemoryStream 类的无法调整大小 的新实例。

1、 读写内存流 ——MemoryStream 类
内存流的Length属性代表了其中存放的数据的真实长度,而 Capacity属性则代表了分配给内存流的内存空间大小。 可以使用字节数组创建一个固定大小的MemoryStream MemoryStream mem = new MemoryStream(buffer);
//这时,无法再设置Capacity属性的大小。

还可以创建只读的内存流对象。
MemoryStream mem = new MemoryStream(buffer,false);

1、 读写内存流 ——MemoryStream 类
MemoryStream 类案例:

运行前效果

运行后效果

2、读写缓存流 ——BufferedStream 类
BufferedStream 类:给另一流上的读写操作添加一个缓冲层。
缓冲区是内存中的字节块,用于缓存数据,从而减少对操作系统的 调用次数。因此,缓冲区可提高读取和写入性能。使用缓冲区可进行读 取或写入,但不能同时进行这两种操作。BufferedStream 的 Read 和 Write 方法自动维护缓冲区。 BufferedStream 可写在某些类型的流周围。它提供从基础数据源 或储存库读取字节以及将字节写入基础数据源或储存库的实现。使用 BinaryReader 和 BinaryWriter 读取和写入其他数据类型。 BufferedStream 用于在不需要缓冲区时防止缓冲区降低输入和输 出速度。如果您读取和写入的大小始终大于内部缓冲区的大小,那么 BufferedStream 可能甚至无法分配内部缓冲区。BufferedStream 也 在共享缓冲区中缓冲读取和写入。假设您几乎始终执行一系列读取或写 入操作,而很少在读取和写入之间切换。

2、读写缓存流 ——BufferedStream 类
BufferedStream 类的构造函数

public BufferedStream(Stream StName);
使用默认的缓冲区大小 4096 字节初始化

public BufferedStream(Stream StName, int bsize);
使用指定的缓冲区大小初始化

案例1:通过缓冲区交换数据

设置 “源文件名”的Enabled属性为false

总结
? File是静态对象,提供对文件的创建、拷贝、移动 和删除等一系列操作 ? File.Create(文件名)可以创建新的文件,并结合 FileStream对象来进行读写操作 ? FileStream 和BinaryReader、BinaryWriter对象结 合起来可对二进制数据进行操作 ? 在C#中指明文件名的时候,要使用转义字符“\\‖ ? 内存流提供无法调整大小的数据流视图,而且只 能向其写入 ? BufferedStream对象对缓冲区进行读写

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

第四章、图型图像GDI编程

CONTENT
?本章主要内容介绍
4.1 4.2 4.3 繪圖的基本觀念 繪圖屬性與方法 繪圖相關類別

本章学习目标:

? ? ? ?

了解System. Drawing 命名空间 掌握矢量图形和绘制对象 可以绘制简单的几何图形 掌握图像的基本处理技术

4.1 什么是GDI+
?首先先了解什么是

GDI 呢?GDI 是从 Windows 95 到 Windows 2000 随附的旧版绘图装置接口 (Graphics Device Interface), 是属于绘图方面的 API (Application Programming Interface)。因为应用程序不能直接控制 硬件, 所以当我们要进行绘图的动作时, 必须透过 GDI 才 能完成。
?那

GDI+ 又是什么呢?GDI+ 是 GDI 的后续产品, 是一 种绘图装置接口, 可将应用程序和绘图硬件分隔, 让我们 能够撰写与装置无关的应用程序。它可以让我们不需注 意特定显示装置的详细数据, 便可在屏幕或打印机显示信 息。我们可以呼叫 GDI+ 类别所提供的方法, 然后这些方 法会适当地呼叫特定的装置驱动程序, 而完成绘图。

4.2 System. Drawing 命名空间
System.Drawing 命名空间包含许多基本与进阶的绘 图类别, 供程序开发者来完成各种绘图功能。本章在此仅 介绍 System.Drawing 命名空间中一些常用的绘图类别。
?

4.2 System. Drawing 命名空间
1、坐标系统
?在「GDI+」中所采用的坐标系统,与平时人们较常用

的坐标系统不同,主要差别在于,一般的二维坐标系,x 轴与y轴分别是往右往上递增(左图),而「GDI+」所采用 的坐标系,x轴与y轴则分别是往右往下递增(右图)

4.2 System. Drawing 命名空间
2、绘图基本单位
在数学定义中,坐标上的最基本元素:「点」,其实是 个长度与宽度都无穷小的概念单位,但是在计算器图学 中,作画的最基本元素是「像素(pixel)」

当我们的屏幕分辨率设定为1024*768时,表示在x轴 横坐标总共可以画1024个「像素」,在y轴纵坐标总 共可以画768个「像素」

4.3 Graphics类别简介
?Graphics 类别是 GDI+ 的核心, 若要绘制任何图形, 都需 要先取得 Graphics对象, 设定它的属性, 呼叫它的方法来完 成绘图的工作。由于 Graphics 类别并未公开其建构子, 故 无法以建构子来建立一个 Graphics 对象, 而是要从您所要 绘图的组件取得一个 Graphics 对象, 其语法如下: Graphics g = 物件.CreateGraphics; ?以上叙述中的对象可为窗体 (Form) 及 Control 类别的衍 生类别 (例如, Label, PictureBox 及TextBox 等...), 当上述 对象呼叫 CreateGraphics 方法后, 会传回一个Graphics 对 象, 您可利用此一 Graphics 对象在建立它的对象上绘图。 例如以下叙述可取得窗体的 Graphics 对象。 Graphics g = this.CreateGraphics;

4.3 Graphics类别简介
1、在FORM的Paint方法中创建 private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; } 2、直接创建 public Form10() { InitializeComponent(); Graphics g; g=this.CreateGraphics(); } 3、由图像创建 Bitmap mybitmap = new Bitmap(@"c:\1.jpg"); Graphics g = Graphics.FromImage(mybitmap);

4.3 Graphics类别简介
4、填充背景颜色案例 private void Form10_Paint(object sender, PaintEventArgs e) { e.Graphics.Clear(Color.Red); } 指定顏色也可以利用RGB來指定,如: 純紅色畫布: e.Graphics.Clear(Color.FromARGB(255,0,0)); 純綠色畫布: e.Graphics.Clear(Color.FromARGB(0,255,0)); 純藍色畫布: e.Graphics.Clear(Color.FromARGB(0,0,255)); 5、表单paint事件绘制图形 private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Black, 3); e.Graphics.DrawLine(drawPen, 10, 10, 300, 100); }

4.4 Pen类别简介
?C# 的绘图至少必须藉助 Graphics 与 Pen 类别对象的协助, 其中 Graphics 对象就好比一块画布, 而 Pen 类别对象就是画 笔了。以下叙述可产生画笔对象, 画笔的线条颜色为黑色, 线 条粗细为3。 drawPen = New Pen(Color.Black, 3); ?绘图方法 Graphics 类别的常用绘图方法有
?DrawLine(直线)、 ?DrawRectangle (矩形)、 ?DrawEllipse (椭圆)、 ?DrawCurve (曲线)、 ?DarwArc (弧线)、 ?DrawPie (扇形)、 ?DrawLines (多边形)、 ?DrawPolygon (封闭多边形) ?DrawBezier (贝兹曲线)等。

4.4 Pen类别简介
?案例1(绘制直线)
请写一程序, 当使用者按一下按键时, 可于窗体绘制一条起点为 (10,10), 终点为(300, 100) 的直线。
private void Form10_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); Pen mypen = new Pen(Color.Red, 5); g.DrawLine(mypen, 10, 10, 300, 100); }

?案例2: (绘制弧线)
將於一個左上角位於 (50, 50), 寬度為 100, 高度為 350 的矩形內, 繪出 一起始角為 0 度, 弧角為120 度的弧線。
private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Red, 3); e.Graphics.DrawArc(drawPen, 50, 50, 100, 350, 0, 120); }

4.4 Pen类别简介
?案例3: (绘制扇形)
左上角位於 (50, 50), 寬度為 100, 高度為 50 的矩形內, 繪出一起始角為 0 度, 弧角為 90 度的扇形。
private void Form10_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics();

e.Graphics.DrawPie(drawPen, 50, 50, 100, 50, 0, 90);
}

?案例4: (绘制折线)
繪出一條起點為 (100, 10), 終點為 (200, 110), 並通過 (120, 70) 及 (160, 30) 兩點的連續線段。
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); Pen drawPen = new Pen(Color.Black, 1); Point p1, p2, p3, p4; p1 = new Point(100, 10); p2 = new Point(120, 70); p3 = new Point(160, 30); p4 = new Point(200, 110); Point[] points = { p1, p2, p3, p4 }; g.DrawLines(drawPen, points); }

4.4 Pen类别简介
?案例5: (绘制多边形)
繪出一個封閉多邊形, 其起點為 (100, 10), 終點為(300, 10), 並通過 (120, 70) 及 (200,110) 兩點, 最後此方法會在起點與終點之間補上一條直線。
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); Pen drawPen = new Pen(Color.Black, 1); Point p1, p2, p3, p4; p1 = new Point(100, 10); p2 = new Point(120, 70); p3 = new Point(200, 110); p4 = new Point(300, 10); Point[] points = { p1, p2, p3, p4 }; g.DrawPolygon(drawPen, points); }

?案例6: (绘制文字)
於座標 (100, 50) 的位置繪製文字
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); Font drawFont = new Font("隶书", 15); SolidBrush drawBrush = new SolidBrush(Color.Blue); g.DrawString("欢迎光临", drawFont, drawBrush, 100, 50); }

4.4 Pen类别简介
? DashStyle属性 取得或设定线条的样式, 此样式必须是 DashStyle 列举型别的 成员
例如, 以下敘述可將 drawPen 畫筆物件的線條樣式設定為虛線。 drawPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

小技巧: 可於程式開頭使用 "using System, Drawing, Drawing2D;“ 先行宣告,以避免鍵入全名。

4.4 Pen类别简介
? StartCap 与EndCap属性
取得或设定线条的端点样式, 此样式必须 LineCap 列举 型别的成员, 如下表所示。
例如, 以下叙述可将 drawPen 画笔对象的线条端点设为箭头及菱形。 drawPen.StartCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; drawPen.EndCap = System.Drawing.Drawing2D.LineCap.DiamondAnchor;

4.4 Pen类别简介
? 综合实验:

4.5 Brush类别简介
?Graphics 类别像是一块画布, Pen 类别像是一支画笔, 但 是这支画笔只具有画直线及外框 (例如, 椭圆形及扇形)的能 力, 若要对某一块区域进行填色的动作, Pen 类别就没有办 法做到了, 而 Brush 类别就是用来对各种封闭图形填色的工 具。针对各种需要, GDI+ 提供了五种 Brush 的衍生类别, 分 别是 ? SolidBrush (单色) ?TextureBrush (材质) ? HatchBrush (预设图案) ? PathGradientBrush (自定义) ? LinearGradientBrush (渐层)等, 以下仅针对 SolidBrush (单色)与 TextureBrush (材质)类别作进一步的介绍。

4.5 Brush类别简介
?案例1——FillRectangle(绘制填充矩形): 在一個左上角位於 (10, 10), 寬度 300, 高度 100 的矩形內填入蓝色。
private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Red, 3); SolidBrush mybrush = new SolidBrush(Color.Blue); e.Graphics.DrawRectangle(drawPen, 10, 10, 300, 100); e.Graphics.FillRectangle(mybrush, 10, 10, 300, 100); }

?案例2——FillRectangle(绘制填充图形): 在一個左上角位於 (10,10), 寬度為 300, 高度為 100 的矩形內填入檔 名為"c:\1.jpg" 的圖檔。
private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Red, 3); TextureBrush mybrush = new TextureBrush(Image.FromFile(@"c:\1.jpg")); e.Graphics.DrawRectangle(drawPen, 10, 10, 300, 100); e.Graphics.FillRectangle(mybrush, 10, 10, 300, 100); }

4.5 Brush类别简介
?案例3——FillEllipse(填充椭圆):
在指定左上角座標 (50, 30), 寬度 (200) 及高度(100) 的矩形內繪製一個填滿 顏色的橢圓。
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); SolidBrush sb = new SolidBrush(Color.Blue); g.FillEllipse(sb, 50, 30, 200, 100); }

?案例4——FillPie(填充扇形):
在指定左上角座標 (50, 10), 寬度 (150) 及高度(100) ,初始角度为0,终止角 度为90的扇形內繪製一個填滿顏色的扇形。
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); SolidBrush sb = new SolidBrush(Color.Yellow); g.FillPie(sb, 50, 10, 150, 100, 0, 90); }

4.5 Brush类别简介
?案例5——FillPolygon(填充多边形):
填充一個封閉多邊形, 其起點為 (100, 10), 終點為(160, 30), 並通過 (120, 70) 及 (200,110) 兩點, 最後此方法會在起點與終點之間補上一條直線。
private void Form10_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); SolidBrush sb = new SolidBrush(Color.Green); Point p1, p2, p3, p4; p1 = new Point(100, 10); p2 = new Point(120, 70); p3 = new Point(200, 110); p4 = new Point(160, 30); Point[] points = { p1, p2, p3, p4 }; g.FillPolygon(sb, points); }

?案例6——HatchBrush(系统图案填充笔触):
填充矩形,前景色为红,背景色为蓝,波形图案; (using System.Drawing.Drawing2D;)
private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Red, 3); HatchBrush mybrush = new HatchBrush(HatchStyle.Wave, Color.Red, Color.Blue); e.Graphics.FillRectangle(mybrush, 10, 10, 300, 100); }

4.5 Brush类别简介
?案例7——LinearGradientBrush(系统复杂渐变填充笔触):
填充矩形,由红色逐渐向黄色混合渐变;

private void Form10_Paint(object sender, PaintEventArgs e) { Pen drawPen = new Pen(Color.Red, 3); LinearGradientBrush mybrush = new LinearGradientBrush(ClientRectangle, Color.Red, Color.Yellow, LinearGradientMode.Vertical); e.Graphics.FillRectangle(mybrush, 10, 10, 500, 500); }

4.5 Brush类别简介
综合练习

4.6 Font类
绘制文本时,可设置字体的样式,字体的大小,以及字体的种类。还 是通过图形对象,在窗体或控件上直接画出,调用Graphics类 DrawString方法。在调用方法前需先设置字体的选项。需要注意的是, 不同的字体绘制出的文本宽度不同。 小实验:写字
小实验

在窗体上直接写出“Windows应用程序设计”,使用隶书、斜体,调 整汉字显示的位置,修改源代码:

Font f = new Font("隶书",24,FontStyle.Italic); Pen p = new Pen(Color.Blue); g.DrawString("Windows应用程序设计",f, p.Brush,50,50);

4.7 坐标平移与缩放
我们看到,前面的例子都是默认以绘图界面的左上角作为原点,坐标 值以像素为单位,画图以左上角为参照点,绘制每一点都要重新计算, 并不方便。因此可以使用Graphics类中对于坐标系统操作的几个方法 进行坐标变换。 小实验:平移
private void Form4_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); g.Clear(Color.White); Pen myPen = new Pen(Color.Red, 3); g.DrawRectangle(myPen, 0, 0, 200, 100); g.DrawEllipse(myPen, 0, 0, 200, 100); g.Dispose(); myPen.Dispose(); }

小实验

/// <summary> /// 坐标移动 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); g.Clear(Color.White); Pen myPen = new Pen(Color.Red, 3); g.TranslateTransform(30, 120); g.DrawRectangle(myPen, 0, 0, 200, 100); g.DrawEllipse(myPen, 0, 0, 200, 100); g.Dispose(); myPen.Dispose(); }

4.7 坐标平移与缩放
小实验:缩放
private void Form4_Paint(object sender, PaintEventArgs e) { Graphics g = this.CreateGraphics(); g.Clear(Color.White); Pen myPen = new Pen(Color.Red, 3); g.DrawRectangle(myPen, 0, 0, 200, 100); g.DrawEllipse(myPen, 0, 0, 200, 100); g.Dispose(); myPen.Dispose(); }
小实验

/// <summary> /// 坐标缩放 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); g.Clear(Color.White); Pen myPen = new Pen(Color.Red, 3); g.ScaleTransform(3, 3);//关键变换语句:放大3倍 g.DrawRectangle(myPen, 0, 0, 200, 100); g.DrawEllipse(myPen, 0, 0, 200, 100); g.Dispose(); myPen.Dispose(); }

4.8 绘制图形
本次实验目标是掌握绘制曲线的基本要领,可以在任意窗体或控件上找到各相关 点,计算绘制曲线,以正弦曲线为例,首先应找到坐标原点,然后找到每一个曲 线上的对应点的坐标,在两点之间画一条直线,如此反复直到曲线末尾。

小实验

实验步骤(1): 先定制坐标轴,确定坐标原点,依次画两条直线分别作为X,Y轴。因为窗体的左上角坐 标为(0,0),在代码中使用的坐标定位都是相对的,相对于窗体的左上角位置。为 了看得清楚,在窗体的四周留出了一部分边缘,使用绝对像素值,将坐标原点定位在 (30,窗体高度-100),按钮的上方。随着窗体大小的变化,横坐标轴根据窗体高度 绘制在不同位置。

4.8 绘制图形
小实验:绘制坐标轴
private void button1_Click(object sender, EventArgs e) { //绘制坐标轴开始 Graphics g = this.CreateGraphics(); Pen myPen = new Pen(Color.Blue, 3); Point oo1 = new Point(30, this.ClientSize.Height - 100); Point oo2 = new Point(this.ClientSize.Width - 50, this.ClientSize.Height - 100); g.DrawLine(myPen, oo1, oo2); Point oo3 = new Point(30, 30); g.DrawLine(myPen, oo1, oo3); 小实验 Font f = new Font("宋体", 12, FontStyle.Bold); g.DrawString("x", f, myPen.Brush, oo2); g.DrawString("y", f, myPen.Brush, 10, 10); //绘制正弦曲线 float x1, x2,y1,y2,a; x1 = x2 = 0; y1 = 0; y2 = this.ClientSize.Height - 100; for (x2 = 0; x2 < this.ClientSize.Width; x2++) { a = (float)(2 * Math.PI * x2 / (this.ClientSize.Width)); y2 = (float)Math.Sin(a); y2 = (1 - y2) * (this.ClientSize.Height - 100) / 2; g.DrawLine(myPen, x1 + 30, (float)y1, x2 + 30, (float)y2); x1 = x2; y1 = y2; } }

4.8 绘制图形
小实验:绘制饼形图 /// <summary> /// 绘制饼形图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param>
小实验

private void button2_Click(object sender, EventArgs e) { Graphics g = this.CreateGraphics(); g.Clear(Color.White); Pen p = new Pen(Color.Blue); Rectangle r = new Rectangle(50, 50, 200, 100); Brush b = new SolidBrush(Color.Blue); g.FillPie(p.Brush, r, 0, 60); g.FillPie(b, r, 60, 150); b = new SolidBrush(Color.Yellow); g.FillPie(b, r, 210, 250); }

4.8 绘制图形
小实验:利用方法 动态绘制饼形图
// <summary> /// 饼形图绘制方法 /// </summary> /// <param name="percent"></param> /// <param name="percolor"></param> private void Fill(int[] percent, Color[] percolor) { Graphics g = this.CreateGraphics(); Rectangle r = new Rectangle(10, 10, 400, 400); Brush b; int beginAngle = 0; for (int i = 0; i <= percent.GetUpperBound(0); i++) { b = new SolidBrush(percolor[i]); g.FillPie(b, r, beginAngle, percent[i]); beginAngle += percent[i]; } } /// <summary> /// 绘制饼形图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { int[] percent = new int[] { 40,100,50,80,70,20};//总数达到360即可 Color[] percolor = new Color[] { Color.Tan, Color .Orange,Color.Red,Color.Black,Color.Blue,Color.BurlyWood}; Fill(percent, percolor); }

小实验

4.9 绘制图形
小实验:利用方法 动态绘制饼形图
// <summary> /// 饼形图绘制方法 /// </summary> /// <param name="percent"></param> /// <param name="percolor"></param> private void Fill(int[] percent, Color[] percolor) { Graphics g = this.CreateGraphics(); Rectangle r = new Rectangle(10, 10, 400, 400); Brush b; int beginAngle = 0; for (int i = 0; i <= percent.GetUpperBound(0); i++) { b = new SolidBrush(percolor[i]); g.FillPie(b, r, beginAngle, percent[i]); beginAngle += percent[i]; } } /// <summary> /// 绘制饼形图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { int[] percent = new int[] { 40,100,50,80,70,20};//总数达到360即可 Color[] percolor = new Color[] { Color.Tan, Color .Orange,Color.Red,Color.Black,Color.Blue,Color.BurlyWood}; Fill(percent, percolor); }

小实验

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

第五章、多线程编程技术

CONTENT
?本章主要内容介绍
5.1 5.2 5.3 计算机线程介绍 System.Threading简介 线程的优先级与锁技术

本章学习目标:

? ? ? ?

理解线程的概念 理解.NET中线程的属性和方法 创建和使用线程 理解线程的特点、优点及使用场合

5.1 线程简介
同时执行所有任务, 时间更少,效率更高
一览无遗

操作系统允许计算机 同时执行多项操作

在同一时间点执行各项进程
人 体 血液循环 程序 (进程) 读 ,写 ? 编译程序 ? 发送/接收邮件 ? 打印文件 ? 其他

5.1 线程简介

线程 1
线程 2

程序 1

线程 3

单独的执行路径
线程 1 线程 2

线程 3
程序 2

5.1 线程简介
? 进程:是应用程序的一个运行例程,是应用 程序的一次动态执行过程。 ? 线程:是进程中的一个执行单元;是操作系 统分配CPU时间的基本单元。
? Windows是一个支持多线程的系统。 ? 一个进程可以包含若干个线程。

5.1 线程简介
在以下情况中可能要使用到多线程:
? ?

程序需要同时执行两个或多个任务 程序要等待某事件的发生,例如用户输入、文件操作、 网络操作、搜索等 后台程序

?

5.1 线程简介
? 多线程:在同一时间执行多个任务的功能,称为多线 程或自由线程。 ? 多线程的优点:可以同时完成多个任务;可以使程序 的响应速度更快;可以让占用大量处理时间的任务或 当前没有进行处理的任务定期将处理时间让给别的任 务;可以随时停止任务;可以设置每个任务的优先级 以优化程序性能。 ? 多线程的缺点: 对资源的共享访问可能造成冲突(对共享资源的访问 进行同步或控制) ;程序的整体运行速度减慢等等。
在C#应用程序中,第一个线程总是Main()方法,因为第一 个线程是由.NET运行库开始执行的,Main()方法是.NET运 行库选择的第一个方法。后续的线程由应用程序在内部启 动,即应用程序可以创建和启动新的线程。

5.2 System.Threading 命名空间
System.Object

System.Delegate

System.ValueType

System.MulticastDelegate

System.Threading.Thread System.Exception

System.Threading.ThreadStart

System.SystemException System.Enum

System.Threading. ThreadPriority

System.Threading. ThreadState

System.Threading. ThreadAbortExcep tion

System.Thread StateException

5.2 System.Threading 命名空间
? 在.NET程序设计中,线程是使用Thread类(或 Timer类(线程计数器)、ThreadPool类(线程 池))来处理的,这些类在System.Threading命名 空间中: using System.Threading; ? Thread类:(实现线程的主要方法)一个Thread 实例管理一个线程,即执行序列。通过简单实例化 一个Thread对象,就可以创建一个线程,然后通 过Thread对象提供的方法对线程进行管理。 ? Timer类:适用于间隔性的完成任务。 ? ThreadPool类:适用于多个小的线程。

1、Thread 类的属性和方法
引用System.Threading 命名空间

Thread 线程实例名 = new Thread(new ThreadStart(方法名));

运行在线程上的方法

只创建但不启动线程

线程实例名.Start();

1、Thread 类的属性和方法
属性
CurrentThread 获取当前正在运行的线程 IsAlive 指示当前线程的执行状态
Abort() 终止线程 Join() 阻塞调用线程,直到某个线程终止时为止 Resume() 继续已挂起的线程 Start() 启动线程 Suspend() 挂起线程 GetDomain 返回当前线程正在其中运行的当前域 Interrupt 中断处于WaitSleepJoin线程状态的线程 ResetAbort 取消为当前线程请求的Abort

方法
Sleep() 将当前线程阻塞指定的毫秒数

Name 获取或设置线程的名称
Priority 获取或设置线程的优先级

CurrentContext 获取线程其中执行的当前上下文
IsBackground 指示线程是否为后台线程

ThreadState 获取或设置线程的当前状态

2、线程的生命周期
线程已创建但并未启动
未开始

线程已启动 线程已恢复
恢复 等待 , 休眠 , 加入 线程被阻止,另一个操作正在进行 开始 I/O 操作完成后运行 在 I/O 操作期间线程被阻止 已开始 I/O 完成

正在运行
暂停

提前中断或特地停止线程
发送 I/O 请求 完成

等待 / 休眠 / 加入

已暂停

已停止

已阻止

暂时中断的线程

5.3 线程的建立与启动
新建一个线程的过程:只需将其声明并为其提供线程起始点 处的方法委托,再用Thread.Start()方法启动该线程 (1)声明: Thread a; (2)实例化 a=new Thread(new ThreadStart(b)); 其中,b为新建过程中执行的过程名。 (3)调用Thread.Start()方法启动该线程 a.Start();

5.3 线程的建立与启动
例题1:线程的建立和启动
using System; using System.Threading; public class A { public void ff()//线程启动时调用此方法 { Console.WriteLine("A.ff()方法在另一个线程上运行!!"); Thread.Sleep(3000);//将线程阻塞一定时间 Console.WriteLine("终止工作线程调用此实例方法!!"); } public static void gg() { Console.WriteLine("A.gg()方法在另一个线程上运行!!"); Thread.Sleep(5000);//将线程阻塞一定时间 Console.WriteLine("终止工作线程调用此静态方法!!"); } }

5.3 线程的建立与启动
例题1:线程的建立和启动
public class B { public static void Main() { Console.WriteLine("***********线程简单示例!***********"); A a=new A(); Thread s1=new Thread(new ThreadStart(a.ff)); s1.Start(); Console.WriteLine("启动新线程ff()方法后,被Main()线程 调用!!"); Thread s2=new Thread(new ThreadStart(A.gg)); s2.Start(); Console.WriteLine("启动新线程gg()方法后,被Main()线程 调用!!"); Console.ReadLine(); } }

5.3 线程的建立与启动
线程的挂起、恢复与终止
线程的挂起(或暂停) (1)调用Thread.Sleep()方法将线程挂起。 注:Sleep()方法指定的时间以毫秒为单位。 (2)调用s1.Suspend() 方法将线程挂起 区别:前者为静态方法,并且使线程立即暂停一定时间;后者为实例方 法,不会使线程立即停止执行,直到线程到达安全点之后,它才将该线 程暂停。 线程的恢复与终止 调用Resume()方法将线程恢复; 调用Abort()方法将线程终止;

其他与操作线程相关的方法
Join():使一个线程等待另一个线程停止 Interrupt():中断处于JoinWaitSleep线程状态的线程。

class SimpleThreadDemo { static void Main(string[] args) { Thread.CurrentThread.Name = “主线程"; Thread objThread = new Thread(new ThreadStart(ActionMethod)); objThread.Name = “子线程"; //启动子线程, 并为该线程执行 ActionMethod objThread.Start(); 输出结果混乱 实例化 objThread 线程并 //这将为主线程执行 ActionMethod 开始执行 ActionMethod() ActionMethod(); } 将由应用程序线程执行 static void ActionMethod() { for(int count = 1; count <= 10 ; count++) { Console.WriteLine(“线程名:" + Thread.CurrentThread.Name); } } }

5.4

线程的优先级

5.4 线程的优先级
线程是根据其优先级来调度的,每个线程都有特定的优先级。 每个线程在创建时其优先级为: ThreadPriority . Normal 线程的优先级定义为ThreadPriority枚举类型,如下表:

5.4 线程的优先级
例题1:
static void Main(string[] args) { Thread objThreadOne = new Thread(new ThreadStart(TaskOne)); Thread objThreadTwo = new Thread(new ThreadStart(TaskTwo)); objThreadOne.Start(); objThreadTwo.Start(); } static void TaskTwo() TaskOne() { index = 5000;index > =4990; index--) for(int count=1;count<=5;count++) { Console.WriteLine(index); Console.WriteLine(count*2); } }

无优先级线程,同时执行,输出无序….

5.4 线程的优先级
例题1:
static void Main(string[] args) { Thread.CurrentThread.Name = “主线程"; Thread objThreadOne = new Thread(new ThreadStart(TaskOne)); objThreadOne.Name = “子线程 1"; Thread objThreadTwo = new Thread(new objThreadTwo.Name = “子线程 2";

ThreadStart(TaskTwo));

}

// 这将启动子线程 objThreadOne.Start(); objThreadTwo.Start(); objThreadTwo.Priority = ThreadPriority.Highest;

将在执行第一个线程前执行 objThreadTwo

5.4 线程的优先级
例题2:使用三个线程显示计数 【代码见下:】 问题:请将代码执行多次,观察每次执行顺序的不同之处。 为什么代码执行是完全不同呢?

【结论:】 如果不将线程按照一定的顺序运行,则线程代码在处理上 将会混乱不堪。

5.4 线程的优先级
例题1:使用三个线程显示计数【代码更改】

【线程优先级小结:】 如果自行提高一个线程的优先级,那么该线程就会相应 的获得更多的CPU时间;通过降低了线程的优先级,该线 程就会被分配到比原来少的CPU时间了。 你可以在一个线程开始运行前或是在它的运行过程中的 任何时候改变它的优先级。理论上你还可以任意的设置每 个线程的优先级,不过一个优先级过高的线程往往会影响 到其他线程的运行,甚至影响到其他程序的运行,所以最 好不要随意的设置线程的优先级。

5.5

线程的同步

使用线程的一个重要方面是同步访问多个线程访问的任何变量。
? 背景:当多个线程共享数据,其中一个或多个线程要修改数 据时,有可能引起数据不统一等问题。 ? 同步:是指在某一时刻只有一个线程可以访问某共享数据。 1、同步的含义 同步问题的产生,主要是由于在高级语言的源代码中,大 多数情况下看起来是一条语句,但在最后编译好的汇编语言机 器码中则会被翻译为许多条语句,从而在操作系统调度时被划 分到不同的时间片中。 例如

? ss

5.5

线程的同步

例如: message += "Hello world!"; 这条语句在C#语法上是一条语句,但在执行代码时,实际上它 涉及到许多操作。需要重新分配内存以存储更长的新字符串, 需要设置变量message使之指向新的内存,需要复制实际文本 等。

5.5

线程的同步

1、在C#中处理同步 通过对指定对象的加锁和解锁可以实现同步代码段的访问。 在.NET的System.Threading命名空间中提供了Monitor类来实 现加锁与解锁。该类中的方法都是静态的。如下表:

锁定机制
线程1

共享资源

缓冲和隔离 程序 线程2

锁定机制保证每次只有一个线程可以访问共享资源

示例

class ThreadLockDemo { static void Main() { Thread.CurrentThread.Name = “主线程"; ThreadLockDemo objDemo = new ThreadLockDemo(); Thread newThread = new Thread (new ThreadStart(objDemo.DoTask)); newThread.Name = “子线程"; newThread.Start(); 实例化 newThread 线程并开始执行 DoTask() objDemo.DoTask(); } void DoTask() { 锁定当前实例 objDemo,在块中完成执行,然后释放对象 lock(this) for(int count = 1; count <= 10 ; count++) { Console.WriteLine(“线程名:" + Thread.CurrentThread.Name); } } }

5.5

线程的同步

1、在C#中处理同步
C#中 lock关键字提供了与Monitoy.Enter和Monitoy.Exit同样的功能, 这种方法用在你的代码段不能被其他独立的线程中断的情况。通过对Monitor 类的简易封装,lock为同步访问变量提供了一个非常简单的方式,其用法如 下: lock(x) { // 使用x的语句 }

lock语句把变量放在圆括号中,以包装对象,称为独占锁或排它锁。 当执行带有lock关键字的复合语句时,独占锁会保留下来。当变量被 包装在独占锁中时,其他线程就不能访问该变量。如果在上面的代码 中使用独占锁,在执行复合语句时,这个线程就会失去其时间片。如 果下一个获得时间片的线程试图访问变量,就会被拒绝。Windows会 让其他线程处于睡眠状态,直到解除了独占锁为止。

5.5

线程的同步

例题1:使用lock同步线程,完成10个线程的取钱工作 【代码见下:】 例题2:两个线程的互相执行过程演示

【代码见下:】

5.5

线程的同步

2、同步时要注意的问题
线程同步非常重要,但只在需要时使用也是非常重要的。因为这 会降低性能。原因有两个: ?首先,在对象上放置和解开锁会带来某些系统开销,但这些系 统开销都非常小。 ?第二个原因更为重要,线程同步使用得越多,等待释放对象的 线程就越多。如果一个线程在对象上放置了一个锁,需要访问该 对象的其他线程就只能暂停执行,直到该锁被解开,才能继续执 行。 ?结论: 在lock块内部编写的代码越少越好,以免出现线程同步错误。 lock语句在某种意义上就是临时禁用应用程序的多线程功能,也 就临时删除了多线程的各种优势。

5.6

线程应用实例

综合例题1: 通过Process类获取系统进程列表。运行界面如下图所示:

总结
? 线程是在共享内存空间中并发的多道执行路径 ? 在 C# 中,是使用 System.Threading 命名空间 中的 Thread 类来创建线程的 ? 线程优先级可以更改为 ThreadPriority 枚举中定 义的一个值 ? C# 中的 lock 关键字是实现线程同步的一种方法 ? 同步的线程称为安全线程 ? 除非绝对必要,否则不要创建线程安全的代码, 因为添加不必要的锁定会降低性能

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

第六章、数据库设计与开发

CONTENT
?本章主要内容介绍
6.1 6.2 6.3 windows和窗体 Visual Stutio .net IDE简介 事件处理

本章学习目标:

? ? ? ?

了解ADO.NET 结构 了解ADO.NET 的组件 使用Command 对象和 Connection 对象 使用ADO.NET 进行事务处理

6.1

ADO.NET 简介

以流的形式从文件中读写数据

6.1 ADO.NET 简介
ORACLE 以流的形式从文件中读写数据 SQL-Server

中央数据库
MS-ACCESS

要管理数据库,需要专门的软件应用程序

6.1 ADO.NET 简介
不同的访问方法和技术 用户计算机向数据库服务器发送请求

中央数据库

客户端

用户需要时,可以随时访问数据

6.1 ADO.NET 简介
.NET Framework

以 ActiveX 数据对象 (ADO) 为基础 以 XML(扩展标记语言)为格式传送和接收数据 System.Data 命名空间

6.1.1 ADO.NET 的优点及主要对象
一、ADO.NET优点

? ? ? ? ?

互操作性 性能 可伸缩性 标准化 可编程能力

6.1.1 ADO.NET 的优点及主要对象
二、ADO.NET对象
1、Connection对象

Connection 对象主要是开启程序和数据库之间的 连结。没有利用连结对象将数据库打开,是无法从数 据库中取得数据的。这个物件在ADO.NET 的最底层, 我们可以自己产生这个对象,或是由其它的对象自动 产生。

6.1.1 ADO.NET 的优点及主要对象
二、ADO.NET对象
2、 Command 对象

Command 对象主要可以用来对数据库发出一些指令, 例如可以对数据库下达查询、新增、修改、删除数据 等指令,以及呼叫存在数据库中的预存程序等。这个 对象是架构在Connection 对象上,也就是Command 对象是透过连结到数据源

6.1.1 ADO.NET 的优点及主要对象
二、ADO.NET对象
3、 DataAdapter对象

DataAdapter 对象主要是在数据源以及DataSet 之间 执行数据传输的工作,它可以透过Command 对象下达 命令后,并将取得的数据放入DataSet 对象中。这个对 象是架构在Command对象上,并提供了许多配合 DataSet 使用的功能。

6.1.1 ADO.NET 的优点及主要对象
二、ADO.NET对象
4、 DataSet对象

DataSet 这个对象可以视为一个暂存区(Cache),可以 把从数据库中所查询到的数据保留起来,甚至可以将整个数 据库显示出来。DataSet 的能力不只是可以储存多个Table 而已,还可以透过DataAdapter 对象取得一些例如主键等的 数据表结构,并可以记录数据表间的关联。DataSet 对象可 以说是ADO.NET 中重量级的对象,这个对象架构在 DataAdapter对象上,本身不具备和数据源沟通的能力;也 就是说我们是将DataAdapter对象当做DataSet 对象以及数 据源间传输数据的桥梁。

6.1.1 ADO.NET 的优点及主要对象
二、ADO.NET对象
5、 DataReader对象

当我们只需要循序的读取数据而不需要其它操作时,可 以使用DataReader 对象。DataReader对象只是一次一笔 向下循序的读取数据源中的数据,而且这些数据是只读的, 并不允许作其它的操作。因为DataReader 在读取数据的 时候限制了每次只读取一笔,而且只能只读,所以使用起 来不但节省资源而且效率很好。使用DataReader 对象除 了效率较好之外,因为不用把数据全部传回,故可以降低 网络的负载。

6.1.2 ADO.NET的结构

6.1.2 ADO.NET的结构

6.1.3 ADO.NET 对数据库的访问(1)
数据已缓存
选择连接和命令 填充 数据源 WinForm

数据适配器 数据库

数据集

数据绑定控件

将数据传递到用户界面时, ADO.NET 采用 XML 格式

6.1.3 ADO.NET 对数据库的访问(2)
对于Insert,Update,Delete 等单向操作
Insert用InsertCommand Delete 用DeleteCommand Update用UpdateCommand 1、用Connection.Open打开数据库

应用程序

2、用DataAdaptor.Command或者 Command.Execute 执行命令 3、关闭数据库

数据库

6.1.3 ADO.NET 对数据库的访问(3)
对于Select的双向操作

1、用Connection .Open建立连接 2、用DataAdaptor.SelectCommand 执行命令

应用程序 DataSet
关闭数据库

DataAdaptor.Fill

数据库

知识点小结
? ? ? ? 引入ADO.NET的原因 ADO.NET的优缺点 ADO.NET的组成结构 ADO.NET对数据库的调用过程

6.1.4 .NET 数据提供程序 1
ADO.NET
DataSet

.NET Framework 数据提供程序
由以下各项组成的集合:

1、SQLClient 2、OLEDB 3、Oracle 4、ODBC

Connection

6.1.4 .NET 数据提供程序 2
1、客户端从服务器请求数据

3、数据集传递给客户端

2、将数据发送到数据集

客户端

数据集

服务器

A、客户端修改数据集
1,2,3是从服务器检索数据的过程 A、B是对数据库修改的过程

B、将修改后的数据 集传递给服务器

6.1.4 .NET 数据提供程序 3
? 用于 SQL Server 的 .NET Framework 数据提供 程序
System.Data.SqlClient 命名空间

仅限于连接 SQL Server 数据库 7.0 或更高版本

6.1.4 .NET 数据提供程序 4
? 用于 OLEDB 的 .NET Framework 数据提供 程序

6.1.4 .NET 数据提供程序 5
? 用于 ODBC 的 .NET Framework 数据提供 程序的命名空间
System.Data.Odbc 命名空间

? 用于 Oracle 的 .NET Framework 数据提供 程序的命名空间
System.Data.OracleClient 命名空间

知识点小结
? 使用的名称空间
– SQL Server – OLE DB – Oracle using System.Data.SqlClient using System.Data.OleDb using System.Data.OracleClient

– ODBC

using System.Data.Odbc

6.1.5 Connection 对象 1
.NET Framework 数据提供程序
SQL 数据提供程序 OLE DB 数据提供程序 Oracle 数据提供程序 ODBC 数据提供程序

Connection 类
SqlConnection OleDbConnection OracleConnection OdbcConnection

在 ADO.NET 中,必须显式关闭连接,才能释放实际的数据库连接

6.1.5 Connection 对象 2
属性 ConnectionString
Database

方法
Close()

Open()

6.1.5 Connection 对象 2

在工具栏之中,添加数据库若干的组件对象

Connection 对象4-3
拖放
<新建连接…>

SqlConnection 从列表中选择 SQL Server 单击“数据”选项 卡 指定用户名和密码 最后选择数据库 sqlConnection1 已添加 如果连接成功,则会显示一则消息

连接数据库步骤
? 连接数据库的步骤:
1、定义连接字符串
没有密码,可省略 Data Source=服务器名;Initial Catalog=数据库名; User ID=用户名;Pwd=密码

2、创建 Connection 对象
SqlConnection connection = new SqlConnection(connString); 连接字符串

3、打开与数据库的连接
connection.Open( );

6.1.5 Connection 对象 4
使用代码编辑器
SqlConnection objSqlConnection = new SqlConnection ("server = SQLDB; uid = sa; pwd = password; database = pubs"); objSqlConnection.Open();

objSqlConnection ? 创建的连接对象名称
SQLDB ? 存储“pubs”数据库的服务器名称,也可以写成IP地址 ,如123.101.220.1 本地服务器可以有以下几种写法: 1、(local) 2、127.0.0.1 3、本地机子名称

uid, pwd ? 用户标识和密码

知识点小结
? Connection对象的作用 ? Connection对象的两种使用方法
– 拖拉控件

– 写代码

? 四种典型的连接方式的应用名称空间和连 接字符串的写法
– SqlConnection – OleDbConnection

– OracleConnection
– OdbcConnection

6.1.6 Command 对象 1
检索和操纵数据

数据库
Command 对象指定要对数据库执行的操作

6.1.6 Command 对象 2
.NET Framework 数据提供程序
SQL 数据提供程序 OLE DB 数据提供程序 Oracle 数据提供程序 ODBC 数据提供程序

Command 类
SqlCommand OleDbCommand OracleCommand OdbcCommand

与数据库建立连接之后,可以使用 Command 对象执行 命令并从数据源返回结果

6.1.6 Command 对象 3
属性

方法
ExecuteNonQuery() 返回受影响的行数
ExecuteReader() 返回DataReader类型值 ExecuteScalar() 返回第一行第一列

CommandText 欲执行的内容,可以是SQL语 句或者存储过程名称 CommandType 命令类型 Connection

Command 对象 6-4
在 CommandText 属性中键入“Insert 拖放 into FlightDetails values()”
SqlCommand 单击“数据”选 项卡 将 sqlConnection1 设置为 sqlCommand1 的连接 已添加 sqlCommand1

6.1.6 Command 对象 6
已添加“FlightDetails”表

单击该表中所有字段旁边的框,以指定要在其中插入新值的列 为每一列指定新值

6.1.6 Command 对象 4
?用SQL 语句的Command设置
SqlCommand objComm=new SqlCommand(); objComm.CommandText="SQL 语句"; objComm.CommandType=CommandType.Text ; objComm. Connection=objConnection;

?用存储过程的Command设置
SqlCommand objComm=new SqlCommand(); objComm.CommandText=―sp_DeleteName"; objComm.CommandType=CommandType. StoredProcedure ; objComm. Connection=objConnection;

Sp_DeleteName是在SQL Server服务器上创建的存储过程

6.1.6 Command 对象 7
使用代码编辑器
SqlCommand objSqlCommand = new SqlCommand(strSQL);
objSqlConnection ? 创建的 Command 对象名称 strSQL ? 任何有效的 SQL 语句

综合示例
? 完成系统登录功能
1、验证管理员的用户名和密码是否存在 2、验证通过,显示管理员主窗体 ? ? ? ? 处理登录按钮的 Click 事件 定义一个 ValidateUser() 方法 需要 Connection 和 Command 对象 分两步实现验证:
1、建立数据库连接 2、验证用户是否存在

用户验证方法框架
ValidateUser() 方法框架
1、验证的结果:通过,不通过

返回值为 bool 型

2、方法需要: 用户名,密码,登录类型

值方式传参

3、不通过的原因: 用户名或密码不存在、其他原因

引用方式传参

public bool ValidateUser(string loginType, string loginId, string loginPwd, ref string message) { }

用户验证方法具体实现
ValidateUser() 方法具体实现

? 第一步:建立数据库连接
– 增加 DBHelper 类

? 第二步:查询用户是否存在
– SELECT COUNT(*) – ExecuteScalar() 方法查询

小结
增加验证用户的代码
// 查询 Student 表使用的 SQL 语句 string sql = string.Format( "SELECT COUNT(*) FROM Student WHERE LogInId='{0}' AND LogInPwd='{1}'",txtLogInId, txtLogInPwd); // 使用的 Command 对象 SqlCommand command = new SqlCommand(sql, DBHelper.connection); DBHelper.connection.Open(); // 执行查询,返回找到的个数 count = (int)command.ExecuteScalar();

总结
? Connection 对象的什么方法用来打开和 关闭数据库连接? ? ExecuteScalar()方法返回什么?

知识点小结
? Command的作用 ? Command的使用前提
– Connection打开

? Command的使用方法
– 命令文本方式 – 存储过程方式

? Command返回值类型
– 返回受影响的行数 – 返回第一行第一列 – 返回DataReader

6.1.7 .NET 中的事务处理 1
确保
成功地进行整个操作或者取消整个操作

关系表
帐单
12439 编号: 12439 2004 年 10 月 30 日期: 2004年10 月 30日 日
编 号

说明

数量

价格

总计

数据库

肥皂 1 肥皂

已成功地完成对一个表的操作, 事务处理是一组数据操作,这些操作要么必须全部成功, 但是关系表尚未更新,因此, 要么必须全部失败,以保证数据的一致性和完整性 数据会出现不一致的现象

55

20 100

6.1.7 .NET 中的事务处理 2
事务处理命令
Begin: 在执行事务处理中的任何操作之前,必须使用 Begin 命令来开始事务处理 ? Commit: 在成功将所有修改都存储于数据库时,才算是提 交了事务处理 ? Rollback: 由于在事务处理期间某个操作失败,而取消事务 处理已做的所有修改,这时将发生回滚
?

6.1.7 .NET 中的事务处理 3

OdbcTransaction

说明
表示对数据源进行的 SQL 事务处理

OleDbTransaction 表示对数据源进行的 SQL 事务处理 OracleTransaction 表示对数据库进行的事务处理 SqlTransaction 表示要对 SQL Server 数据库进行的 Transact-SQL 事务处理

6.1.8 SqlTransaction 类 1
SqlTransaction 类表示要对数据源进行的事务处理

属性

方法

Connection

Save( ) Commit( ) Rollback( )

6.1.8 SqlTransaction 类 2
在 ADO.NET 中实现事务处理时执行的步骤顺序

private void button1_Click(object sender, EventArgs e) { SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=(local);Initial Catalog=school;User ID=sa"; conn.Open(); SqlTransaction sqltran = conn.BeginTransaction(); string sqlstring = "insert into student(sno,sname) values(3390220,'张三')"; SqlCommand comm = new SqlCommand(sqlstring, conn); comm.Transaction = sqltran; int p = comm.ExecuteNonQuery(); sqltran.Commit(); comm.Dispose(); comm.Clone(); conn.Dispose(); conn.Close(); }

应用程序示例 7-1
在 VS.NET 2005 中新建一个名为示例 1 的基于 Windows 的项目。将默认窗体重命名为 form3.cs

用户界面

请使用并恢复school1数据库, 案例见下

总结 2-1
? .NET framework 中的 ADO.NET 是一组类, 允许应用程序与数据库交互,以便检索和 更新信息 ? DataSet 和 .NET 数据提供程序是 ADO.NET 的两个主要组件 ? 每种 .NET 数据提供程序都是由以下四个对 象组成:
– Connection – Command – DataAdapter

总结 2-2
? Connection 对象用于在应用程序和数据库 之间建立连接 ? Command 对象允许向数据库传递请求、检 索和操纵数据库中的数据 ? 事务处理是一组数据操作,这些操作要么 必须全部成功,要么必须全部失败,以保 证数据的一致性和完整性

6.2 ADO.NET-查询和检索数据

回顾
? .NET framework 中的 ADO.NET 是一组允许应用程序与 数据库交互,以便检索和更新信息的类 ? DataSet 和 .NET 数据提供程序是 ADO.NET 的两个主要 组件 ? 每种 .NET 数据提供程序都是由以下四个对象组成: – Connection – Command – DataAdapter – DataReader ? Connection 对象用于建立应用程序与数据库之间的连接 ? Command 对象允许向数据库传递请求,检索和操纵数 据库中的数据 ? 事务处理是一组数据操作,这些操作要么必须全部成功, 要么必须全部失败,以保证数据的一致性和完整性

问题
? ? ? ? ? ADO.NET的两个主要组成部分是? ADO.NET中数据提供程序包含哪几个对象? Command返回值有哪几种? 对数据库的操作一般分成哪几个步骤? 事务处理一般分成哪几步骤?举例说明在什么情 况下采用事务处理?

目标
? 理解并使用数据集对象 ? 使用 DataAdapter 对象 ? 使用 DataReader对象

6.2.1 DataSet 1
DataSet 类的层次结构 是存储从数据库检索到的 不直接与数据库交互 DataSet 数据的对象

DataTableCollection DataSet DataTable 是零个或多个表对象的集合,这 些表对象由数据行和列、约束和 DataColumnCollection 有关表中数据关系的信息组成 既可容纳数据库的数 据,也可以容纳非数 DataRowCollection 据库的数据源 DataRow

DataColumn

6.2.1 DataSet 2
DataSet 层次结构中的类 类 DataTableCollection DataTable DataColumnCollection 说明 包含特定数据集的所有 DataTable 对象 表示数据集中的一个表 表示 DataTable 对象的结构

DataRowCollection
DataColumn

DataRow

表示 DataTable 对象中的实际数 据行 表示 DataTable 对象中列的结构 表示 DataTable 对象中的一个数 据行

DataSet 的结构2-2
DataTableCollection 数据表集合 GradeTable ClassTable StudentTable DataColumnCollection 数据列集合 DataTable 数据表

id name class sex 1 小菲 小薇 小强 5 3 6 女 女 男 2 3

DataColumn 数据列 DataRow 数据行

DataRowCollection 数据行集合

数据集的工作原理
请求数据

传递数据

发送数据

数据集
修改数据集
客户端 提交修改后的数据

如何创建 DataSet 对象
? 创建一个 DataSet
– 可以指定一个数据集的名称 – 如果不指定名称,则默认被设为"NewDataSet"
可选的

DataSet 数据集对象 = new DataSet("数据集的名称字符串");

DataSet dataSet = new DataSet(); DataSet dataSet = new DataSet(“MySchool”); //MySchool为数据集名称

数据集的类型
使用类型化数据集访问列 使用非类型化数据集访问列 从 dsEmployees 数据集的 Emp 表 中第一个记录返回 EmpName 列, 类型化数据集 非类型化数据集 然后将其存储在 employeeName string employeeName; 字符串变量中 employeeName =
dsEmployees.Tables["Emp"].Rows[0]["EmpName"];

数据集的类型

集合返回 EmpName 列 string employeeName; 使用 Tables 它是一个生成类,继承 表和列只能以集合的形式 employeeName = dsEmployees.Emp[0].EmpName; 了基类 DataSet 的所有
方法、事件和属性 公开,而不能用于借助 XML 结构文件派生新类

1.数据集的类型——类型化数据集

第一步:创建数据库连接对象

Data Source=(local);Initial Catalog=school;User ID=sa

1.数据集的类型——类型化数据集
第二步:添加 新数据源

1.数据集的类型——类型化数据集

第三步:建立 类型化数据集

1.数据集的类型——非类型化数据集

第一步:建立表及主键

2.数据集的类型——非类型化数据集

第二步:建立表的主键唯一性约束

2.数据集的类型——非类型化数据集

第三步:建立表的外键约束

使用 C# 代码创建数据集
? 数据集实例是由 DataSet 构造函数创建的 ? 数据集的名称是可选的,不需要指定 ? 如果没有指定名称,则以默认名称 NewDataSet 创建数据集
DataSet empDS = new DataSet("EmployeeDetails"); DataSetName 用于获取或设置当前数据集的名称

属性

说明

Tables

用于检索数据集中包含的表集合

方法
Clear HasChanges

说明
清除数据集中包含的所有表的所有行

返回一个布尔值,指示数据集是否更 改了

3.数据集的类型——编码实现类型化数据集(1)
private void Form5_Load(object sender, EventArgs e) { string connstring = "Data Source=(local);Initial Catalog=school;User ID=sa"; SqlConnection connection = new SqlConnection(connstring); connection.Open(); string sqlstring = "select * from student"; SqlCommand mycom = new SqlCommand(sqlstring, connection); SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = mycom; //设置DataSet对象 DataSet ds = new DataSet(); adapter.Fill(ds); //开始循环读取DataSet中的数据表信息 for (int i = 0; i < ds.Tables[0].Rows.Count; i++) { //label1.Text += ds.Tables[0].Rows[i]["sname"].ToString()+"\n"; label1.Text += ds.Tables[0].Rows[i][2].ToString() + "\n"; } //释放数据库连接资源 connection.Dispose(); connection.Close(); connection = null; }

3.数据集的类型——编码实现类型化数据集(2)
基于C#的异构数据源数据整合技术

Access数据源student表结构

SQL Server数据源student表结构

6.2.2方法 DataTable、DataColumn和 DataRow 1 说明
AcceptChanges 提交对该表所做的所有修改

NewRow 添加新的 DataRow ? 数据集中的数据以 DataTable 对象的形式存储 事件 类属于 System.Data说明 ? DataTable 命名空间
ColumnChanged RowChanged 修改该列中的值时激发该事件 成功编辑行后激发该事件

属性

说明

RowDeleted Columns

表示列的集合或 DataTable 包含的 成功删除行时激发该事件 DataColumn

Constraints 表示特定 DataTable 的约束集合 DataTable objStudentTable = new DataTable("Students"); DataSet 表示 DataTable 所属的数据集 PrimaryKey

表示作为 DataTable 主键的字段或 创建 DataTable 对象的实 DataColumn

表示行的集合或 DataTable 包含的 DataSet studentDS = new DataSet(); Rows DataRow DataTable objStudentTable = studentDS.Tables.Add("Students"); 创建 DataTable 的实例,然后 返回一个布尔值,指示数据集是否更改 添加到数据集的 Tables 集 HasChanges

6.2.2 DataTable、DataColumn和DataRow 2 ? DataColumn 对象定义 DataTable 的列 使用多个 DataColumn 对象创建 DataTable ? DataTable 的 Columns 属性含有对 DataColumn 对象的引用 属性 说明
表示一个值,指示对于该表中的行,此列是否允许 AllowDBNull null 值 = new DataTable("Students"); DataTable objStudentTable ColumnName 表示指定 DataColumn 的名称 DataColumn objStudentNumber = objStudentTable.Columns.Add (" StudentNo ",typeof(Int32)); DataType 表示指定 DataColumn 对象中存储的数据类型 objStudentNumber.AllowDBNull = false; DefaultValue 表示新建行时该列的默认值 objStudentNumber.DefaultValue = 25; Table 表示 DataColumn 所属的 DataTable 的名称 objStudentTable.Columns.Add("StudentName",typeof(Int32)); objStudentTable.Columns.Add("StudentMarks",typeof(Double)); Unique 表示 DataColumn 的值是否必须是唯一的

//定义表结构,为Students表添加属性列 DataTable studt = new DataTable("student"); DataColumn sno = new DataColumn(); sno = studt.Columns.Add("sno", typeof(int)); sno.AllowDBNull = false; sno.Unique = true;

6.2.2 DataTable、DataColumn和DataRow 3
属性 说明
Item

? DataRow DataTable 中的实际数据 RowState 对象表示 表示行的当前状态
DataColumn ssex = new DataColumn(); ssex = studt.Columns.Add("ssex", typeof(string)); ssex.AllowDBNull = false; ssex.Unique = false; ssex.DefaultValue = "男";

DataColumn sname = new DataColumn(); sname = studt.Columns.Add("sname", typeof(string)); sname.AllowDBNull = false; sname.Unique = false;

在 DataTable 对象中新建 DataRow 表示 DataRow 的指定列中存储的值 表示用于创建 DataRow 的 DataTable 的 名称

Table

方法

说明

DataColumn sbirthday = new DataColumn(); 用于提交自上次调用了 AcceptChanges 之 AcceptChanges sbirthday = studt.Columns.Add("sbirthday", typeof(string)); 后对该行所做的所有修改 sbirthday.DefaultValue = DateTime.Now.ToLongDateString();
studt.Columns.Add("sclass", typeof(string)); DataRow dr = studt.NewRow(); dr["sno"] = 1001; dr["sname"] = "张三"; dr["ssex"] = "男"; dr["sbirthday"] = "1980-2-1"; dr["sclass"] = "98002"; studt.Rows.Add(dr);

Delete

Deletes the DataRow 用于删除 DataRow 用于拒绝自上次调用了 AcceptChanges 之 后对 DataRow 所做的所有修改

RejectChanges

for (int i = 0; i < studt.Rows.Count; i++) { label1.Text = "编号:" + dr["sno"].ToString() + ";姓名:" + dr["sno"].ToString() + ";性别:" + dr["ssex"].ToString() + ";生日:" + dr["sbirthday"].ToString() + ";班级:" + dr["sclass"].ToString(); }

如何定义Datatable的主键
? 表中的主键用于对记录进行唯一标识 ? DataTable 的 PrimaryKey 属性接受含有一 objStudentTable.PrimaryKey = new DataColumn[]{objStudentTable.Columns["StudentNo"]}; 个或多个 DataColumn 对象的数组
设置单个列为 DataTable 的主键 objStudentTable.PrimaryKey = new DataColumn[] { objStudentTable.Columns["StudentNo"], objStudentTable.Columns["StudentName"] }; 为 DataTable 对象设置复合主键

DataTable 的约束
是对表中数据施加的 决定表中可以存储 限制或规则集 约束的类型 的数据

Constraint
ForeignKeyConstraint UniqueConstraint

用于维护数据的正确性和有效性

案例见下

DataView
说明 用作 DataTable 中存储的数据的表示层 Item 用于从指定的表中获取一行数据 提供对 DataTable 进行排序、筛选和搜索的 自定义视图 用于获取或设置表达式,该表达式用于筛选 RowFilter DataView DataView 中查看的行 DataView objStudentView = 可以在 new DataView(objStudentTable); 允许 WinForms 控件进行数据绑定 objStudentView.RowFilter = "StudentMarks > 60"; RowStateFilter 用于获取 DataView 可用于查看 DataTable 的行状态筛选器 中存储的数据的子集 for(int ctr =0; ctr < objStudentView.Count; ctr++) Table 用于表示源 DataTable { MessageBox.Show(objStudentView[ctr]["StudentNo"].ToString()); 方法 说明 } AddNew 向 数据绑定 DataView 添加新行
Delete 创建 DataView 并对该视图应用某种筛选器 用于删除指定索引处的行 是为了在控件上显示数据库表中存储的数据,而将 应用程序的控件与数据表的行进行绑定的过程

属性

知识点小结
? ? ? ? 使用DataSet的优点及使用场合 DataSet的层次结构 操纵DataSet中指定行中特定列的值 在程序中向DataSet中动态添加表、字段、 值、约束 ? 利用DataView筛选数据

6.2.3 为什么使用 DataAdapter
? 如何将数据库的数据放在 DataSet 中?
DataSet 数据集
DataAdapter 数据适配器

数据库

DataSet 数据集

数据库

Connection 数据库连接

6.2.3 DataAdapter 对象 1
DataAdapter 数据命令 填充 DataTable DataRow 集合 非永久连接 DataColumn 集合 Constraint 集 合 数据源

DataSet
脱机数据库

6.2.3 DataAdapter 对象 2
.NET 提供程序及其 DataAdapter 类
属性 说明 .NET Framework 数据提 Connection 类 供程序 类的属性和方法 决定在把行复制到 DataTable 中时对行所 DataAdapter AcceptChangesDuringFill
容纳一个集合,该集合提供返回行和数据 OleDbDataAdapter OLE DB 数据提供程序集之间的主映射
TableMappings

SQL 数据提供程序

做的修改是否可以接受

SqlDataAdapter

方法 说明 OracleDataAdapter Oracle 数据提供程序 用于添加或刷新数据集,以便使数据集与
Fill

ODBC 数据提供程序
FillSchema

数据源匹配

用于在数据集中添加 DataTable,以便与数 据源的结构匹配

OdbcDataAdapter

Update

将DataSet里面的数值存储到数据库服务器 上

如何填充数据集
? 使用 DataAdapter 对象填充数据集
DataAdapter 的 Fill() 方法

DataSet 数据集

Connection

数据库

使用 Connection 连接数据源

使用 Fill() 方法填充 DataSet 中的表

填充数据集
1
创建 SqlDataAdapter 对象 SqlDataAdapter 对象名 = new SqlDataAdapter(查询用sql语句, 数据库连接);

2

填充 DataSet

DataAdapter对象. Fill(数据集对象, "数据表名称字符串");

如何保存 DataSet 中的数据
? 把数据集中修改过的数据提交到数据源
DataSet 数据集
Connection DataAdapter 的 Update() 方法

数据库

dataAdapter.Update(dataSet,"Teacher");

调用前,要先设置更新需要的相关命令 可以使用 SqlCommandBuilder 对象

案例: SQLDataAdapter完成对数据库的 删除修改和插入

SqlCommandBuilder 对象
? 利用 SqlCommandBuilder 对象能够自动 生成:
– INSERT 命令 – UPDATE 命令 – DELETE 命令

SqlCommandBuilder builder = new SqlCommandBuilder(已创建的DataAdapter对象);

保存 DataSet 中的数据
? 步骤:
1
自动生成用于更新的相关命令 SqlCommandBuilder builder = new SqlCommandBuilder(已创建的DataAdapter对象);

2

将 DataSet 的数据提交到数据源 DataAdapter对象. Update(数据集对象, "数据表名称字符串");

SqlCommandBuilder builder = new SqlCommandBuilder(dataAdapter); dataAdapter.Update(dataSet,“Student");

小结

? DataAdapter 的作用是什么?
? 使用什么方法填充 DataSet ?

? 使用什么方法将 DataSet 中数据提交到 数据库?

知识点小结
? ? ? ? DataAdapter的作用 DataAdapter的组成 DataAdapter的属性、方法 使用DataAdapter对数据的选择、添加、删 除和修改

为什么使用 DataReader
? 怎样读取数据库的数据?
使用 DataReader 对象

应用程序 姓名: 张明 学校: 福州大学 成绩: 优秀 毛毛,浙江水专,优秀

数 据 库

6.2.4 DataReader 1
DataReader .NET 数据提供程序及其 DataReader 类 .NET Framework 数据提供 查询 程序
SQL 数据提供程序 OLE DB 数据提供程序

只读 只进记录集

DataReader 类
SqlDataReader OleDbDataReader

Oracle 数据提供程序
ODBC 数据提供程序

只读和只进访问 OracleDataReader 需要永久连接
OdbcDataReader

数据源

操作数据
? 如何对数据库的数据进行增删改?
使用 ExecuteNonQuery() 方法

应用程序
姓名: 林伟彬 学校: 福州大学 成绩: 良好 毛毛,浙江水专,良好

数 据 库

综合练习1
添加组合框中的学号信息

? 处理窗体 Load 事件 ? 使用 DataReader 读取学号信息

综合练习2

DataReader 的主要成员
? DataReader 的主要成员:
属性 HasRows 方法 Read Close 说明 是否返回了结果 说明 前进到下一行记录 关闭 DataReader 对象

DataReader 使用步骤小结
? 使用 DataReader 检索数据的步骤:
1、创建 Command 对象 2、调用 ExecuteReader() 创建 DataReader 对象 3、使用 DataReader 的 Read() 方法逐行读取数据 4、读取某列的数据,(type)dataReader[ ] 5、关闭 DataReader 对象

注意:DataReader 使用后必须关闭

获取某列的值: 方法一:指定列的索引,从0开始 方法二:指定列名

总结
? 在 DataSet 对象内表示的数据是数据库的部分或 全部的断开式内存副本 ? DataAdapter 对象用来填充数据集和用更新集到 数据库,这样方便了数据库和数据集之间的交互 ? 类型化数据集对象是 DataSet 类的派生类的实例, 这些类都基于 XML 结构 ? DataTable 表示一个内存数据表,而 DataColumn 表示 DataTable 中列的结构 ? DataView 是 DataTable 中存储的数据的表示层 ? DataReader 对象提供只进、只读和连接式数据访 问,并要求使用专用的数据连接

6.3 dataGridView 控件 — 显示和操作数据

回顾
? 在 DataSet 对象内表示的数据是数据库的部分或 全部的断开式内存副本 ? DataAdapter 对象用来填充数据集和用更新集到 数据库,这样方便了数据库和数据集之间的交互 ? 类型化数据集对象是 DataSet 类的派生类的实例, 这些类都基于 XML 结构 ? DataTable 表示一个内存数据表,而 DataColumn 表示 DataTable 中列的结构 ? DataView 是 DataTable 中存储的数据的表示层 ? DataReader 对象提供只进、只读和连接式数据访 问,并要求使用专用的数据连接 ? DataReader 对象提供检索强类型化数据的方法

目标
? 了解 DataGridView 控件常见的属性和方法 ? 掌握 DataGridView 的数据绑定 ? 掌握在 DataGridView 控件中插入、更新和 删除数据 ? 掌握定制DataGridView界面

为什么使用 DataGridView
? 怎样显示 DataSet 中的数据呢?
使用 DataGridView

数 据 集

DataGridView 控件
? DataGridView 控件
强大而灵活地显示数据 轻松定义控件外观 像 Excel 表格一样方便 一行代码实现数据绑定

可视化操作

简介

DataGridView 是 .NET 2.0 中的一个新控件,是针对 .NET 1.x 中功 能较差的标准 DataGrid 控件而设计的。Matthew MacDonald 在本文 中论述了许多改进,包括:DataGridView 支持大量自定义和细致的格 式设置、灵活的大小调整和选择、更好的性能以及更丰富的事件模型

数据使用者

数据提供程序

VS .NET - DataGridView

DataSet
姓名 年 龄 性 别

列表框

职员编号

E1003 01

Ian S.

29

M

E1003 02 有效数据使用者 E1003 多个可视组件 03

文本框

Ricky M. Diana

32
26

M
F

DataGridView 控件功能 DataGridView 是 WinForms 中最通用、最强大
和最灵活的数据控件

|

支持编辑功能

可相互绑定
数据源

职员编号

姓氏

名字

E001 E002

Clive Klayton

Grant Knight

DataGridView 控件创建

拖放

出现 DataGridView 控件

DataGridView 控件的属性和方法 2-1

DataGridView
属性
AllowUserToAddRows AllowUserToDeleteRows

说明
获取或设置一个值,该值指示是否向用户显示 添加行的选项。 获取或设置一个值,该值指示是否允许用户从 DataGridView 中删除行。

AllowUserToOrderColumns

获取或设置一个值,该值指示是否允许通过手 动对列重新定位。
整列的大小。

AllowUserToResizeColumns 获取或设置一个值,该值指示用户是否可以调 AllowUserToResizeRows DataSource Columns 获取或设置一个值,该值指示用户是否可 以调整行的大小。
获取或设置 DataGridView 所显示数据的数据源 获取一个包含控件中所有列的集合。

为 DataGridView 控件绑定数据 2-1
DataGridView
DataSet
DataView DataTable

.

数组 列表

为 DataGridView 控件绑定数据 2-2
绑定方法1: dbgPassenger.DataSource = objDataSet.Tables["Passenger"].DefaultView; 绑定方法2: dbgBooks.DataSource = objDataSet; dbgBooks.DataMember = "Titles"; 绑定方法3: dbgBooks.SetDataBinding(objDataSet,"Titles");

DataGridView 控件绑定数据案例

/*第一种方式:DataSet DataSet ds = mydatabase.GetDataSet("select * from student"); dataGridView1.DataSource = ds.Tables[0];*/ //第二种方式:DataTable DataTable dt = mydatabase.GetDataTable("select * from student"); dataGridView1.DataSource = dt;

插入、更新和删除记录 3-1
需要将这些更新发送到数据 库以进行保存

数据库

使用 DataAdapter.Update() 方法

任何更新

插入、更新和删除记录 3-2
DataAdapter.Update() 方法 调用相应的命令

DataGrid 控件

修改后的数据集
姓名 lan S. Ricky M. Diana 性别 M M F

任何修改
出生日期 12/10/1989 10/31/1974 1/3/1978

电话号码 34907782 29380774 33804483

通过参数集合传递修改后的行

6.3 DataGridView 控件创建
基本的数据操作【案例1】 通过本案例您将学 习了解到: 1、 DataGridView 的数据绑定技术; 2、 DataGridView 的更新和删除技术

6.3 DataGridView 控件创建
基本的数据操作【案例2】 通过本案例您将学 习了解到: 1、 DataGridView 的数据绑定技术; 2、数据上下顶底的 移动技术; 3、 DataGridView 的更新和删除技术

4、window控件的综 合应用

6.3 DataGridView 控件创建
复杂数据操作【案例3】

6.3 DataGridView 控件创建
复杂数据操作【案例】

6.3 DataGridView 控件创建
复杂数据操作【案例】

6.3 DataGridView 控件创建
复杂数据操作【案例】

6.3 DataGridView 与存储过程

定义: 将常用的或很复杂的工作,预先用SQL语句写好并用一个指定的名称存 储起来, 那么以后要叫数据库提供与已定义好的存储过程的功能相同的 服务时,只需调用execute,即可自动完成命令。 存储过程的优缺点: 优点: 1.减少网络传输,节约时间 2.速度快 3.出错的概率小 4.可利用服务器的一些特殊的资源 缺点: 1.交互性差 2.不够灵活 3.商业逻辑层与数据库在一起,不易移植. 附: 存储过程有个致命的缺点:就是可能如果要更换数据库(比如:从sql server到oracle) 的话,你的存储过程就都要重写了。所以它过分依赖数据库端,假如你要做一个工程, 是可以的,但是如果你要做一个产品,或者以后可能经还会用到这个工程的代码的话, 建议少使用。所以任何一种开发方法总有优缺点,不要过分依赖

6.3 DataGridView 与存储过程
1.维护方便,如果你把所需要执行的操作代码写在程序当中,那么要做一些小修改,也要对程 序做修改并重新编译,对于用户来讲,这个时候就需要对程序加以深度测试(你说只修改了一 点点,但是用户不会为你的一句话而冒风险的),存储过程相对来讲就简单得多,毕业逻辑上分 离出来了,即使要进行审核,成本也远比程序要低得多; 2.重用性,一个设计好的存储过程,任何符合其输入和输出的程序都可以共用,同样也改善了 维护工作,如果有10个程序要用到这个存储过程,那要做些修改,自然修改一个存储过程所花 费的成本比程序要低得多; 3.分工的明确化.存储过程如果设计得当,就可以通过一个中间的适配器(Adapter)来调用,如 此在将来进行数据库的迁移等,就可以把相应的工作交给具体的熟悉相关数据库等业务的 人员,而不是所有的开发人员都要进行了解.就好比MSSQL/Oracle/DB2类似的,如果数据库 操作脚本写在程序当中,那么迁移的工作量将会是所有可能的程序.如果是通过存储过程,那 就可以交给相应的DBA等来完成; 4.存储过程的预编译,可能性使得语句的执行更优化,特别是对于类似Oracle等具备某些高 级优化能力的,那么包括其既定的执行计划等可以带来相当的性能提升; 5.对于需要多次访问数据的复杂操作.如果写在程序当中,那么就需要不断的或者大量的提 取数据库当中的数据到程序当中进行运算,而如果是使用存储过程,那么就减少了应用程序 与数据库的交互次数,如此的消耗明显要低得多.

6.3 DataGridView 与存储过程
本实验操作较为复杂,需要提前准本的内容包括: 1、数据库——Grade_Sys数据库; 2、建立类——BuessinessLayer.cs(业务逻辑文件) 3、建立类——DataBaseLayer.cs (数据库操作文件)

6.3 DataGridView 与存储过程
通过本案例,您将学习到: ?动态生成datagridview列表信息内容 ?C#开发系统分层思想; ?存储过程参数的输入与返回值的获取 ?复杂存储过程的开发设计

详细代码设计部分见下:

总结
? 在数据库编程中使用数据绑定控件时, DataGrid 控件是 Visual Studio .NET 中提供的最通用、最强大和最灵活的 控件 ? DataGrid 控件以表的形式显示数据,并根据需要支持数据 编辑功能,如插入、更新、删除、排序和分页 ? 使用 DataSource 属性为 DataGrid 控件设置一个有效的数 据源 ? 调用 Update() 方法来执行相应的插入、更新和删除操作 时,将执行 DataAdapter 的 InsertCommand、 UpdateCommand 和 DeleteCommand 属性 ? 定制DataGrid界面

ADO.NET 小结
ExecuteNonQuery() DataSet 应用程序 ExecuteScalar()

内存
Fill() Update()

Read() ExecuteReader()

Command 对象

DataReader 对象

DataAdapter 对象

Connection 对象

.NET 数据提供程序
数 据 库

总结2-1
? 在 DataSet 对象内表示的数据是数据库的部分或全部的 断开式内存副本 ? DataAdapter 对象用来填充数据集和用更新集到数据库, 这样方便了数据库和数据集之间的交互 ? 类型化数据集对象是 DataSet 类的派生类的实例,这些 类都基于 XML 结构 ? DataTable 表示一个内存数据表,而 DataColumn 表示 DataTable 中列的结构 ? DataView 是 DataTable 中存储的数据的表示层 ? DataReader 对象提供只进、只读和连接式数据访问,并 要求使用专用的数据连接 ? DataReader 对象提供检索强类型化数据的方法

总结2-2
? 在数据库编程中使用数据绑定控件时, DataGridView 控件是 Visual Studio .NET 中提供的最 通用、最强大和最灵活的控件 ? DataGridView 控件以表的形式显示数据,并根据需 要支持数据编辑功能,如插入、更新、删除、排序和 分页 ? 使用 DataSource 属性为 DataGridView 控件设置一 个有效的数据源 ? 调用 Update() 方法来执行相应的插入、更新和删除 操作时,将执行 DataAdapter 的 InsertCommand、 UpdateCommand 和 DeleteCommand 属性 ? 定制DataGridView界面

中央数据库

ORACL E

SQLServer

MS-ACCESS

要管理数据库,需要专门的软件应用程序

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn

第七章、 WinForms网络编程

CONTENT
?本章主要内容介绍
7.1 7.2 7.3 windows和窗体 Visual Stutio .net IDE简介 事件处理

本章学习目标:

? 了解组网 ? 通过 WinForms访问 Internet ? 通过 WinForms进行套接字编程

组网简介 3-1
打印?

打印?

组网是指将网络中的计算机连接在一起 可共享打印机以供网络中所有用户使用
打印机在物理上连接至单台计算机 连接在网络上的多位用户

降低了为每个用户采购打印机而带来的成本, 打印? 充分利用资源

组网简介 3-2
组网是将许多设备(如计算机、打印机和工作站)连接成一个网络以共享资源
` `

WinForms网络编程中用到 System.Net 和 System.Net.Sockets 命名空间

System.Net 命名空间主要处理高层的操作,例如下载和上载文件

`

`

System.Net.Sockets 包含执行低层操作的类, 处理用于让计算机之间高效通信的代码 ` `
共享资源

组网简介 3-3
网络套接字 使用数据包和 IP 地址
`

电源插座
服务器侦听端

IP 地址
发送请求

唯一地标识 Internet 上的每台电脑 口
电度表 低层的路由协议可将数据分成很小的数据 一旦连接上客户端,则 包并通过网络将它们发送到某个地址。
服务器连接就已建立

IP(Internet协议)

具有唯一的地址

TCP(传输控制协议 ) 客户端

可靠传输数据的高层协议

UDP(用户数据报协议)

支持快速、无连接、不可靠的数据包传输 服务器

访问 Internet 6-1
客户端应用程序 万维网是一个客户端/服务器应用程序, Internet 应用程序 借助浏览器对世界各地的 Web 服务器上存储的数据进行访问 服务器应用程序 请求文件并从特定 URL 接收文件 WebClient 类

不能继承 具有一些方法可用于从 URI 标识的任何本 地 Intranet 或 Internet 资源发送和接收数 据。

访问 Internet 6-2
模式标识符标识用于通信的协议 服务器标识 符可标识 DNS 主机名 或 TCP 地 址 路径标识符 从服务器上 查找请求的 信息 URI 用于标识 Internet 上的资源以 及通信协议

可选查询字符串将信息从客户端 传递至服务器 http://msdn.microsoft.com/library/default.asp?url=/library/enus/cpguide/html/cpconrequestingdata.asp

访问 Internet 6-3
System.Net
WebClient
发送或接收来自统一 资源标识符的数据

WebRequest

向统一资源标识符发 出请求 得到统一资源标识符 发出的响应

WebResponse

访问 Internet 6-4
2.将下列名称空间添加至窗体。 WebClient 类的属性和方法 创建如下所示的窗体 using System.Net; using System.IO;

属性

方法
DownloadData() DownloadFile() OpenRead() OpenWrite() UploadFile()

3.在按钮 btnGo 的 Click 事件中添加下列代码,以从文本框输入的 URI 中 读取数据。 private void btnGo_Click(object sender, System.EventArgs e) { WebClient client = new WebClient(); Stream strm = client.OpenRead(txtURI.Text); StreamReader sr = new StreamReader(strm); string line; while((line = sr.ReadLine()) != null) { lstRecd.Items.Add(line); } strm.Close(); }

Headers QueryString

访问 Internet 6-5
抽象类

属性
Method RequestUri
WebRequest 类

方法
Create() GetRequestStream( ) 使用传递至其 Create() 方法的 URI 值创 GetResponse() 建特定于协议的实例
封装与服务器连接、发送请求和接收响应 的详细信息

对 WebRequest 实例调用的 GetResponse() 方法帮助向 Internet 资源 发出请求

访问 Internet 6-6
使用WebRequest 和 WebResponse 类实现“执行”按钮的功 能
private void btnGo_Click(object sender, System.EventArgs e) { 抽象类 WebRequest wrq =WebRequest.Create(txtURI.Text); WebResponse wrs=wrq.GetResponse(); Stream strm=wrs.GetResponseStream(); 提供来自 URI 的响应。 WebResponse 类 sr = new StreamReader StreamReader(strm); string line; while((line=sr.ReadLine()) != null) 客户端应用程序不创建 WebResponse 对象,因 { 为在对 WebRequest 实例调用 GetResponse() lstRecd.Items.Add(line); 方法时会创建它们。 } strm.Close(); }

套接字编程
System.Net.Sockets名称空间包含允许直接发送TCP网络请求或侦听特定端口 上的TCP网络请求的相关类
System.Net.Sockets
Socket

大量的方法和属性用于网络连接 提供客户端连接,用于创建和使用 TCP 网络服务 可用于从 TCP 网络客户端侦听连接 可用于为 UDP 客户端创建连接(它是 TCP 的备用协议,在本地网络上使用)

TcpClient

TcpListener

UdpClient

使用 TCP 类
? TCP ( Transfers Control Protocol ,传输控制协 议)类包含连接两个点并在这两个点之间发送数 据的方法 ? 一个点由 IP 地址和端口号组成。现有协议具有定 义好的端口号 ? HTTP 使用端口号 80;SMPT 或电子邮件使用端 口号 25; FTP 使用端口号 21 ? Internet Assigned Number Authority (IANA, Internet 编号分配管理机构)负责为这些有名的 服务分配端口号 ? TCP 提供有保证的传送、错误更正和数据缓冲 ? TcpClient 类具有通过网络连接、发送和接收流数 据的简单方法

TcpClient 对象
方法 属性 Close 说明 说明

关闭所有 TCP 连接并释放与 ReceiveBufferS TcpClient 获取或设置接收缓冲区的大小 关联的 ize 资源 SendBufferSize 获取或设置发送缓冲区的大小 Connect 将使用主机名和端口号将客户端连接至 远程 TCP 主机 GetStream 返回用于发送和接收数据的 NetworkStream

TCPListener 类的方法
方法 说明 TcpListener 类具有侦听进入的 TCP连接的 AcceptSocket 返回与远程客户端通信的套 方法,其中Start() 方法用于开始侦听进入的 接字 连接请求,并且将把进入的连接放置在队列 中,直到调用Stop() 为止。AcceptSocket() AcceptTcpClient 接受暂挂的连接请求 或AcceptTcpClient() 方法可用于从进入的连 Start 开始侦听进入的连接请求 接请求队列中提取连接。 Stop 关闭侦听器

TCP 类应用程序实例 8-1

将以下两个名称空间添加至窗体
using System.Net.Sockets; using System.IO ; 在窗体的声明区域中声明下列变量 public TcpListener tcpListener; private NetworkStream networkStream ; private StreamReader streamReader ; private StreamWriter streamWriter ; public Socket socketForClient;

TCP 类应用程序实例 8-2
private void btnstart_Click(object sender, System.EventArgs e) { tcpListener = new TcpListener(1234); 在按钮 btnstart 的 Click 事 tcpListener.Start(); MessageBox.Show("服务器已启动") ; 件中添加下列代码 socketForClient = tcpListener.AcceptSocket(); networkStream = new NetworkStream(socketForClient); streamWriter = new StreamWriter(networkStream); streamReader = new StreamReader(networkStream); try { if (socketForClient.Connected) { MessageBox.Show("客户端已连接"); string line = streamReader.ReadLine(); txtServerClient.Text=line; line=line.ToUpper()+ "!"; streamWriter.WriteLine(line); txtServerServer.Text=line; streamWriter.Flush() ; } } catch (Exception ae) { Console.WriteLine(ae.ToString()) ; } }

TCP 类应用程序实例 8-3
在按钮 btnRecd 的 Click 事件中添加下列代码,以连续将数据传输到客户端
private void btnRecd_Click(object sender, System.EventArgs e) { try { if (socketForClient.Connected) { string line = streamReader.ReadLine(); txtServerClient.Text=line; line=line.ToUpper()+ "!"; streamWriter.WriteLine(line); txtServerServer.Text=line; streamWriter.Flush() ; } } catch (Exception ae) { Console.WriteLine(ae.ToString()) ; } }

TCP 类应用程序实例 8-4
在窗体 Form1 的 Closing 事件中添加下列代码,以关闭所有连接

private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { DialogResult answer=MessageBox.Show (“这将关闭服务器。您是否确实要 关闭它?","提示框",MessageBoxButtons.YesNo ); if (answer==DialogResult.Yes) { Application.Exit(); } if (answer == DialogResult.No) { e.Cancel =true; } socketForClient.Close(); }

TCP 类应用程序实例 8-5
控件 名称 按钮 btnSend

将以下两个名称空间添加至窗体
using System.Net.Sockets; using System.IO ;

声明下列变量
TcpClient myclient; private NetworkStream networkStream ; private StreamReader streamReader ; private StreamWriter streamWriter ;

TCP 类应用程序实例 8-6
在窗体的构造函数中添加代码,以在特定端口建立与服务器的连接 public Form1() { InitializeComponent(); try { myclient = new TcpClient("localhost", 1234); } catch { MessageBox.Show("未能在 {0}:999 处连接至服务器 ", "localhost"); return; } networkStream = myclient.GetStream(); streamReader = new StreamReader(networkStream); streamWriter = new StreamWriter(networkStream); }

TCP 类应用程序实例 8-7

在命令按钮 btnSend 的 Click 事件中编写下列代码

private void btnSend_Click(object sender, System.EventArgs e) { txtServer.Text="" ; if(txtClient.Text=="") { MessageBox.Show("请在文本框中输入内容"); txtClient.Focus(); return ; } try { string s; streamWriter.WriteLine(txtClient.Text); streamWriter.Flush(); s= streamReader.ReadLine(); Console.WriteLine("正在读取消息") ; txtServer.Text=s; } catch(Exception ee) { MessageBox.Show(“从服务器中读取时产生异常"+ee.ToString()); }

}

TCP 类应用程序实例 8-8
在窗体 Form1 的 Closing 事件中添加下列代码以关闭资源 private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { streamReader.Close() ; streamWriter.Close() ; networkStream.Close(); }

UDP 类
? 当应用程序中速度和性能需求比可靠性更重要时, 开发人员可以使用 UDP ( User Datagram Protocol ,用户数据报协议) ? 与 TcpClient 相比, UdpClient 类的接口更小且更 简单。 ? UdpClient 不包含返回网络流以进行读写的方法 ? 因为 UDP 是无连接协议,所以与远程主机的连接 方法 是在发送和接收数据时进行的 Close()

Receive() Send()

P2P 技术简介
? P2P,即英文Peer-to-Peer的缩写,译为对 等互联或点对点技术。P2P是一种用于在 不同PC用户之间,不经过中继设备直接交 换数据或服务的技术,它允许Internet用 户直接使用对方的文件。网络中的任意用 户都可以直接连接到其他用户的计算机, 并进行文件的交换,而不需要连接到服务 器上再进行浏览与下载。因为消除了中间 环节,P2P技术使得网络上的沟通变得更 容易、更直接 ? 它建立在TcpListener以及TcpClient这两个

P2P 应用程序示例 5-1
应用程序示例演示了如何从一点向另一点发送文件 1. 创建一个名为“Example 2”的 Windows 应用程序 2. 将文件名“Form1.cs”更改为“frmSendFile.cs”

3. 设计如图所示的窗体。将文本框分别命名为 txtHostName 和 txtPort ,并且将按钮命名为 btnSendFile

此应用程序打开与服务器的 TCP 连接, 并向它发送此C# 源代码

发送应用程序

P2P 应用程序示例 5-2
private void btnSendFile_Click(object sender, System.EventArgs e) { //TcpClient 对象可指定主机名和端口 TcpClient objTcpClient = new TcpClient(txtHostName.Text, 4. 在项目中添加命名空间。 Int32.Parse(this.txtPort.Text)); //为 TcpClient 对象创建新的流 NetworkStream objNetworkStream = objTcpClient.GetStream(); using // System.Net; 在打开模式下添加文件 using FileStream System.Net.Sockets; objFileStream = File.Open("..\\..\\frmSendFile.cs",FileMode.Open); 读取字节 using // System.IO; int data =objFileStream.ReadByte(); while(data != -1) { 5. 在 btnSendFile 按钮的 Click 事件中添加代码。 //Writing bytes to the stream objNetworkStream.WriteByte((byte)data); data = objFileStream.ReadByte(); } 代码使用主机名和端口号创建 TcpClient //关闭所有打开的流 objFileStream.Close(); objNetworkStream.Close(); objTcpClient.Close(); } 6. 生成应用程序。

P2P 应用程序示例 5-3
在连接的另一侧,File Receive 应用程序会在文本框中显示已接收的文件
1. 新建一个名为“Example 3”的新 Windows 应用程序。 2. 将文件名 Form1.cs 更改为 frmOrdersReport.cs。 3. 设计一个带有名为 txtDisplayFile 文本框的窗体。 4. 在项目中添加命名空间。 using System.Net; using System.Net.Sockets; using System.IO;

P2P 应用程序示例 5-4
private void frmFileReceive_Load(object sender, System.EventArgs e) { //指定机器的 IP 地址 IPAddress objIPAddress = IPAddress.Parse("169.254.25.129"); //新建侦听器 TcpListener objTcpListener = new TcpListener(objIPAddress,2112); //启动 Listener 对象 objTcpListener.Start(); //接受客户端请求 5. 在 TcpClient frmFileReceive 窗体的 = Load 事件中添加代码。 objTcpClient objTcpListener.AcceptTcpClient(); //取得流 NetworkStream objNetworkStream = objTcpClient.GetStream(); StreamReader objStreamReader = new StreamReader(objNetworkStream);string result = objStreamReader.ReadToEnd(); //在文本框中显示内容 6. 生成应用程序。 this.txtDisplayFile.Text = result.ToString(); objTcpClient.Close(); objTcpListener.Stop(); }

P2P 应用程序示例 5-5
运行应用程序:
1. 执行 Send File 应用程序并输入所示的主机名和端口号。

2. 执行 File Receive 应用程序,同时转到 Send File 应用程序,并单击按钮。 下面的输出结果显示在 File Receive 应用程序中。 文件接收应用程序

总结
? 组网是将许多设备(如计算机、工作站和打印机) 连接成一个网络以共享资源
? 统一资源标识符用于标识 Internet 上的资源以及 通信协议 ? WebClient 类从特定 URI 请求并接收文件 ? WebRequest 类与服务器连接、发送请求和接收 响应 ? 端口是特定机器上已编号的套接字。服务器进程 侦听端口,直到有客户端与其连接为止 ? TcpClient 类有可用于连接、发送和接收流数据的 方法;TcpListener 类具有侦听进入的 TCP 连接的

Windows程序设计
--------------基于C#语言

主讲教师:钱 哨 本课学时:72课时 联系方式:qianshao@bjjtxy.bj.cn