Post on 23-Mar-2022
第 章
365 桌面提醒模块
( 自学视频、源程序:配套资源\mr\1\)
无论是在生活中,还是在工作中,小到个人、企事业单位,大到国家,都
会制定计划,并且有些计划是十分重要的。可繁忙的工作和较快的生活节奏,
也许会让您偶尔地“健忘”。当您打开电脑,开始一天的工作时,若有一款桌面
提醒模块软件按照您事先设定的程序,给您一个温馨的提示,将是一个不错的
选择。本章就来开发这样一个功能齐全并有着良好交互性的桌面提醒模块软
件——365 桌面提醒模块。通过本章的学习,读者能够学到以下内容:
多线程处理任务
控件绑定到 List<T>
集合和对象初始化器
ADO.NET 操作数据库
为应用程序添加托盘图标
通过修改注册表实现程序自动运行
使用扩展方法为系统类型添加新功能
·2·
CC#典型模块精解
Note
1.1 365 桌面提醒模块概述
365 桌面提醒模块软件可以根据用户事先的设置提供“自动检查”服务,即根据设置的提
前提醒天数,自动查询指定天数内将要执行的计划。另外,若开启“实时提醒”功能,它还会
定期弹出“提示气泡”,主动提醒用户。365 桌面提醒模块用起来十分简单,几乎不影响其他办
公软件的正常使用。该软件主要由提醒设置、计划录入、计划查询、计划统计和定时关机 5 部
分组成,下面分别介绍。
1.1.1 提醒设置流程
提醒设置的核心内容是设置“提前提醒天数”,因为该软件的“自动检查”和“实时提醒”
功能都要读取“提前提醒天数”这个数据。若启用“自动检查”功能,则软件启动后,程序会
自动检索指定天数内将要执行的计划,否则程序不检索计划数据;若启用“实时提醒”功能,
则软件启动后,程序会根据设定的“提醒间隔”向用户弹出“提示气泡”,主动提示近期要执
行的计划信息。在上述功能设置完毕后,就可以保存数据了。提醒设置的流程图如图 1.1 所示。
提 醒 设 置
保 存 数 据
是 否 实 时 提 醒
提 前 提 醒 天 数
启 动 计 时 器
禁 用 计 时 器
是
是 否 自 动 检 查
检 索 计 划 数 据
不 检 索 计 划 数 据
否
是
否
输 入 提 醒 间 隔
图 1.1 提醒设置流程图
1.1.2 计划录入流程
在初次使用该软件时,需要添加计划任务(其中必须包括计划标题、计划种类和执行日期)。
在输入计划信息完成之后,就可以保存数据。若需要对已添加的计划信息进行修改,则首先选
定要修改的计划信息记录,然后修改计划任务,最后保存数据;若需要删除计划任务,则首先
选择要删除的计划任务,然后执行删除操作。在执行删除操作时,系统会弹出“是否删除”的
提示,当确认删除时,该数据记录将被删除掉。计划录入的流程图如图 1.2 所示。
·3·
Note
第 1 章 365 桌面提醒模块
计 划 录 入
否
执 行 添 加 操 作 输 入 计 划 任 务 保 存 数 据
选 择 计 划 任 务 修 改 计 划 任 务
选 择 计 划 任 务 执 行 删 除 操 作
是 否 删 除
删 除 数 据
是
图 1.2 计划录入流程图
1.1.3 计划查询流程
计划查询有两种方式(按天数或内容查询),可以选择其中任意一种,然后执行查询操作。
在查询结果中(在有查询结果的情况下)双击某条记录,可以执行“处理计划”操作。若当前
计划已被处理过(如计划已经执行),则可以修改处理内容,最后保存数据;若当前计划未被
执行过,则可以录入处理内容,最后保存数据。计划查询的流程图如图 1.3 所示。
计 划 查 询 双 击 某 条 记 录
保 存 数 据
按 天 数 查 询
按 内 容 查 询
查 询 结 果
是 否 已 处 理 过 是 修 改 处 理 内 容
添 加 处 理 内 容否
图 1.3 计划查询流程图
1.1.4 计划统计流程
计划统计用于查询本年度计划任务的执行情况。可分为已按期执行的计划和未按期执行的
计划两类。选择其中的一类,然后执行查询操作,最后程序显示出查询结果。计划统计的流程
图如图 1.4 所示。
计 划 统 计
已 按 期 执 行
未 按 期 执 行
执 行 查 询 操 作 显 示 查 询 结 果
图 1.4 计划统计流程图
1.1.5 定时关机流程
定时关机首先要设置关机时间、关机类型(包括关机、重启、注销和显示提示信息 4 种)
和执行周期(可以是每天执行,也可以是固定每周几执行);然后户设置是否启用“定时关机”
·4·
CC#典型模块精解
Note
功能,若启用该功能,则计算机系统会按照“预设时间”关机,否则不会定时关机;最后需要
保存这些定时关机参数,定时关机的流程图如图 1.5 所示。
定 时 关 机
是
设 置 关 机 类 型
设 置 关 机 时 间
是 否 启 用 定 时 关 机
设 置 执 行 周 期
生 成 定 时 关 机 参 数 保 存 数 据
图 1.5 定时关机流程图
1.1.6 程序预览
365 桌面提醒模块主要由 10 个界面组成,包括托盘菜单、启动提示窗口、“定时关机”窗
口、提示气泡界面、提醒设置界面、计划录入界面、计划查询界面、“计划处理”窗口、计划
统计界面、历史查询界面。下面将介绍其中的 5 个主要界面。
“提示气泡”功能会定时弹出包含将要执行的计划信息的窗口以提醒用户,如图 1.6 所示。
本程序为了使用户操作方便,在桌面的右下角添加了一个“托盘菜单”,其中包括“打开
窗口”、“系统设置”和“退出程序”等命令,如图 1.7 所示。
图 1.6 提示气泡界面 图 1.7 托盘菜单
计划录入是整个系统的主要数据来源,其界面如图 1.8 所示,从中可以添加、修改和删除
计划信息。
计划查询界面用于查询近期将要执行的计划任务,如图 1.9 所示。可以按照提前天数查询,
也可以按照计划内容关键字查询。在查询结果中双击某一条计划信息,还可以打开“处理计划”
窗口,在该窗口中对计划的执行进行简单的说明。
图 1.8 计划录入界面 图 1.9 计划查询界面
·5·
Note
第 1 章 365 桌面提醒模块
“定时关机”窗口用于设置计算机系统定时关机的各种参数,包括关机时间、关机类型、
执行周期和是否启用定时关机等,如图 1.10 所示。
图 1.10 “定时关机”窗口
1.2 数据库设计
365 桌面提醒模块采用 Microsoft Access 2000 创建数据库,其名称为 PlanRemind(对应的
物理文件名称为 PlanRemind.mdb),其中包含 3 张数据表,分别用来存储定时关机参数、提醒
参数设置和计划任务信息,如图 1.11 所示。
图 1.11 PlanRemind 数据库的结构及说明
1.3 公共类设计
为了提高代码的重用率、加强代码的集中化管理,本软件将数据绑定功能和一些特殊属性
封装在自定义类中。下面对这些自定义类进行详细介绍。
·6·
CC#典型模块精解
Note
1.3.1 封装数据值和显示值的类
为了将 DataGridView 控件的 DataGridViewComboBoxColumn 列的数据值转换为显示值,
需要定义两个属性,分别存储该列的 ValueMember 和 DisplayMember 属性值。这两个自定义属
性被封装在 CalFlag 类中,其详细代码如下:
01 class CalFlag //该类封装了两个特殊属性
02 {
03 public string DisplayText //存储 DisplayMember 属性值
04 {
05 get; //获取数据
06 set; //设置数据
07 }
08 public string DataValue //存储 ValueMember 属性值
09 {
10 get; //获取数据
11 set; //设置数据
12 }
13 }
1.3.2 绑定和显示数据的类
为了在 DataGridView 控件的 DataGridViewComboBoxColumn 列中显示数据,本软件将
List<CalFlag>实例绑定到 DataGridViewComboBoxColumn 列;另外,为了更加清晰地查看
DataGridView 控件中的数据记录,本软件实现了在 DataGridView 控件中隔行换色显示数据记
录。这两个功能被封装在 ExtendDataGridView 自定义类中,该类封装了两个扩展方法,其具体
代码如下:
01 static class ExtendDataGridView
02 {
03 /// 转换 DataGridViewComboBoxColumn 列的数据值为显示值
04 /// <param name="dgvcbxColumn">DataGridViewComboBoxColumn 列</param>
05 /// <param name="strValueMemberName">数据值</param>
06 /// <param name="strDisplayMemberName">显示值</param>
07 /// <param name="items">集合</param>
08 public static void ConvertValueToText(this DataGridViewComboBoxColumn
dgvcbxColumn, string strValueMemberName, string strDisplayMemberName, ICollection items)
//声明一个扩展方法
09 {
10 dgvcbxColumn.DataSource = items; //设置数据源
11 dgvcbxColumn.ValueMember = strValueMemberName; //设置数据值
12 dgvcbxColumn.DisplayMember = strDisplayMemberName; //设置显示值
13 }
14 /// 在 DataGridView 控件中隔行换色显示数据记录
15 /// <param name="dgv">DataGridView 控件</param>
16 /// <param name="color">偶数行的颜色</param>
17 public static void AlternateColor(this DataGridView dgv, Color color)
18 {
·7·
Note
第 1 章 365 桌面提醒模块
19 dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
//设置选定模式为整行
20 foreach (DataGridViewRow dgvr in dgv.Rows) //遍历所有的数据行
21 {
22 if (dgvr.Index % 2 == 0) //若是偶数行
23 {
24 dgvr.DefaultCellStyle.BackColor = color; //设置偶数行背景色
25 }
26 }
27 }
28 }
1.4 提 醒 设 置
1.4.1 提醒设置功能概述
提醒设置提供了两个重要的自动服务功能:一是软件启动后,自动检索指定天数内将要执
行的计划任务;二是软件按照指定的时间间隔弹出“提示气泡”。这两种功能的启用都是在提
醒设置界面中操作完成的,提醒设置界面的运行效果如图 1.12 所示。
最小值为 0,表示当天
最小值为 0.01 小时,即 36 秒
图 1.12 提醒设置界面
1.4.2 提醒设置界面设计
将应用程序默认的 Form1 窗体重命名为 Frm_Main;在该窗体上部的工具栏位置添加一个
PictureBox 控件,命名为 pic_CueSetting,用来作为“提醒设置”按钮;在该窗体的下部添加一
个 Panel 控件,命名为 panel_CueSetting,并在该 Panel 控件中添加若干控件,用来显示和设置
提醒信息。该 Panel 控件中添加的主要控件如表 1.1 所示。
·8·
CC#典型模块精解
Note
表 1.1 提醒设置界面用到的控件及说明
控 件 类 型 控件 ID 主要属性设置 用 途
lab_Days 默认设置 该控件的文本用于对“提前提醒天
数”这个概念进行解释
lab_AutoRetrieve 默认设置 该控件的文本用于对“自动检查”
这个概念进行解释
nud_Days Value属性设置为3 设置“提前提醒天数”
nud_TimeInterval Mininum属性设置为0.01;
Value属性设置为4 设置提醒间隔
chb_IsAutoCheck Checked属性设置为true
设置系统启动自动检查最近未执行
的计划任务
chb_IsTimeCue Checked属性设置为true 设置系统是否具有实时提醒的功能
button1 默认设置 实现保存数据的操作
1.4.3 打开提醒设置界面
在窗体的工具栏中单击“提醒设置”按钮,程序将设置 panel_CueSetting 控件为可见状态,
而设置其他界面的 Panel 控件为不可见状态。“提醒设置”按钮的 Click 事件代码如下:
01 private void pic_CueSetting_Click(object sender, EventArgs e)
02 {
03 panel_PlanRegister.Visible = false; //计划录入界面不可见
04 panel_PlanSearch.Visible = false; //计划查询界面不可见
05 panel_PlanStat.Visible = false; //计划统计界面不可见
06 panel_HisSearch.Visible = false; //历史查询界面不可见
07 panel_CueSetting.Visible = true; //提醒设置界面可见
08 //检索提醒设置数据表
09 OleDbDataAdapter oleDa = new OleDbDataAdapter("Select top 1 * from tb_CueSetting",oleConn);
10 DataTable dt = new DataTable(); //创建 DataTable 实例
11 oleDa.Fill(dt); //把数据填充到 DataTable 实例
12 if (dt.Rows.Count > 0) //若存在数据
13 {
14 DataRow dr = dt.Rows[0]; //获取第一条数据
15 nud_Days.Value=Convert.ToDecimal(dr["Days"]); //获取提前天数
16 chb_IsAutoCheck.Checked = Convert.ToBoolean(dr["IsAutoCheck"]);
//设置是否自动检查
17 chb_IsTimeCue.Checked = Convert.ToBoolean(dr["IsTimeCue"]); //设置是否实时提醒
18 nud_TimeInterval.Value = Convert.ToDecimal(dr["TimeInterval"]);//读取时间间隔
19 }
20 }
1.4.4 保存提醒设置
首先输入“提前提醒天数”,因为软件的“自动检查”功能和“实时提醒”功能都要读取
“提前提醒天数”这个数据;然后设置自动检查、实时提醒和时间间隔;最后单击“确定”按
钮保存提示设置。具体实现代码如下:
·9·
Note
第 1 章 365 桌面提醒模块
01 private void button1_Click(object sender, EventArgs e)
02 {
03 //创建命令对象
04 OleDbCommand oleCmd = new OleDbCommand("SELECT top 1 * FROM tb_CueSetting",
oleConn);
05 if (oleConn.State != ConnectionState.Open) //若数据库连接未打开
06 {
07 oleConn.Open(); //打开数据库连接
08 }
09 OleDbDataReader oleDr = oleCmd.ExecuteReader(); //创建只读数据流
10 //定义插入 SQL 语句
11 string strInsertSql = "INSERT INTO tb_CueSetting VALUES(" + Convert.ToInt32(nud_
Days.Value) + "," + chb_IsAutoCheck.Checked + "," + chb_IsTimeCue.Checked + "," + Convert.ToDouble
(nud_TimeInterval.Value)+")";
12 //定义更新 SQL 语句
13 string strUpdateSql = "UPDATE tb_CueSetting set Days = " + Convert.ToInt32(nud_
Days.Value) + ",IsAutoCheck = " + chb_IsAutoCheck.Checked + ",IsTimeCue = " + chb_IsTimeCue.Checked +
",TimeInterval = " + Convert.ToDouble(nud_TimeInterval.Value);
14 //获取本次要执行的 SQL 语句
15 string strSql = oleDr.HasRows ? strUpdateSql : strInsertSql;
16 oleDr.Close(); //关闭只读数据流
17 oleCmd.CommandType = CommandType.Text; //设置命令类型
18 oleCmd.CommandText = strSql; //设置 SQL 语句
19 if (oleCmd.ExecuteNonQuery() > 0) //若执行 SQL 语句成功
20 {
21 MessageBox.Show("设置成功!"); //弹出成功提示框
22 if (chb_IsTimeCue.Checked)
23 {
24 //设置 Timer 控件的触发频率
25 timer1.Interval = Convert.ToInt32(nud_TimeInterval.Value * 3600 * 1000);
26 timer1.Enabled = true; //启动计时器
27 }
28 else
29 {
30 timer1.Enabled = false; //禁用计时器
31 }
32
33 }
34 else //若执行失败
35 {
36 MessageBox.Show("设置失败!"); //弹出失败提示框
37 }
38 oleConn.Close(); //关闭连接
39 }
·10·
CC#典型模块精解
Note
1.5 计 划 录 入
1.5.1 计划录入功能概述
计划录入是 365 桌面提醒模块软件的核心数据来源,系统所有的业务都围绕着计划展开,
计划的内容包括计划标题、计划种类、执行日期和计划内容。计划录入界面的运行效果如图 1.13
所示。
标题必须填写
图 1.13 计划录入界面
1.5.2 计划录入界面设计
在 Frm_Main 窗体上部的工具栏位置添加一个 PictureBox 控件,命名为 pic_PlanRegister,
用来作为“计划录入”按钮;在该窗体的下部添加一个 Panel 控件,命名为 panel_PlanRegister,
并在该 Panel 控件中添加若干控件,用来输入计划信息。该 Panel 控件中添加的主要控件如
表 1.2 所示。
表 1.2 计划录入界面用到的控件及说明
控 件 类 型 控件 ID 主要属性设置 用 途
txt_PlanTitle Enabled属性设置为false 输入计划标题
dtp_ExecuteTime Enabled属性设置为false 选择计划执行日期
cbox_PlanKind Enabled属性设置为false,DropDownStyle
属性设置为DropDownList 选择计划种类
rtb_PlanContent Enabled属性设置为false 输入计划内容
dgv_PlanRegister Columns属性添加若干项(详见源代码);
SelectionMode属性设置为FullRowSelect 显示计划信息
button2 Text属性设置为“添加” 激活并清空各种控件
button3 Text属性设置为“保存” 保存修改或添加的数据
button4 Text属性设置为“删除” 删除计划信息
·11·
Note
第 1 章 365 桌面提醒模块
1.5.3 打开计划录入界面
在窗体的工具栏中单击“计划录入”按钮,程序将设置 panel_PlanRegister 控件为可见状态,
而设置其他界面的 Panel 控件为不可见状态。“计划录入”按钮的 Click 事件代码如下:
01 OleDbDataAdapter oleDa = null; //声明OleDbDataAdapter 类型的引用
02 private void pic_BriRegister_Click(object sender, EventArgs e)
03 {
04 //计划录入界面可见,其他界面不可见
05 panel_CueSetting.Visible = false;
06 panel_PlanStat.Visible = false;
07 panel_PlanSearch.Visible = false;
08 panel_HisSearch.Visible = false;
09 panel_PlanRegister.Visible = true;
10 //创建 OleDbDataAdapter 的实例
11 oleDa = new OleDbDataAdapter("Select * from tb_Plan", oleConn);
12 DataTable dt = new DataTable(); //创建数据表对象
13 oleDa.Fill(dt); //把数据填充到数据表对象
14 dgv_PlanRegister.DataSource = dt; //DataGridView 控件绑定数据源
15 dgv_PlanRegister.AlternateColor(Color.LightYellow); //DataGridView 控件实现隔行
换色显示数据
16 }
1.5.4 添加计划任务
若要添加一个新的计划任务,首先必须单击“计划录入”界面上的“添加”按钮,这时程
序将激活和清空界面上的控件,并将程序当前的操作状态设置为“添加”。“添加”按钮的 Click
事件代码如下:
01 private void button2_Click(object sender, EventArgs e)
02 {
03 blIsEdit = false; //表示当前操作状态为“添加”
04 ActivationControl(true); //激活当前界面上用于输入计划信息的控件
05
06 RestUI(); //重置界面上用于输入计划信息的控件
07 }
ActivationControl 方法用于设置当前界面上某些控件的状态。它有一个 bool 类型的参数,
当该参数值为 false 时,当前界面上用于输入计划信息的控件处于禁用。当该参数值为 true 时,
当前界面上用于输入计划信息的控件处于激活状态。其代码如下:
01 private void ActivationControl(bool blValue)
02 {
03 txt_PlanTitle.Enabled = blValue; //设置计划标题控件的状态
04 cbox_PlanKind.Enabled = blValue; //设置计划种类控件的状态
05 dtp_ExecuteTime.Enabled = blValue; //设置执行日期控件的状态
06 rtb_PlanContent.Enabled = blValue; //设置计划内容控件的状态
07 }
RestUI 方法重新初始化用于输入计划信息的控件。其代码如下:
01 private void RestUI()
·12·
CC#典型模块精解
Note
02 {
03 txt_PlanTitle.Text = ""; //清空计划标题文本框
04 cbox_PlanKind.Text = "一般计划"; //初始化计划种类
05 dtp_ExecuteTime.Value = DateTime.Today; //初始化执行日期
06 rtb_PlanContent.Text = ""; //清空计划内容
07 }
·13·
Note
第 1 章 365 桌面提醒模块
1.5.5 保存计划任务
单击“添加”按钮,程序将设置当前的操作状态为“添加”,然后在当前界面的相关控件
中输入计划信息,最后单击“保存”按钮即可保存计划任务;若要对已有的计划信息进行修改,
首先在当前界面左侧的 DataGridView 控件中选择要修改的记录,该记录的信息就会显示在当
前界面右侧的相关控件中,从中修改相应信息,然后单击“保存”按钮,即可实现保存数据。
“保存”按钮的 Click 事件代码如下:
01 private void button3_Click(object sender, EventArgs e)
02 {
03 string strSql = String.Empty; //定义存储 SQL 语句的字符串
04 DataRow dr = null; //定义数据行对象
05 DataTable dt = dgv_PlanRegister.DataSource as DataTable; //获取数据源
06 oleDa.FillSchema(dt, SchemaType.Mapped); //配置指定的数据架构
07 string strCue = string.Empty; //定义提示字符串
08 if (txt_PlanTitle.Text.Trim() == string.Empty)
09 {
10 MessageBox.Show("标题不许为空!"); //提示标题不许为空
11 txt_PlanTitle.Focus();
12 return;
13 }
14 if (blIsEdit) //若是修改操作状态
15 {
16 dr = dt.Rows.Find(dgv_PlanRegister.CurrentRow.Cells["IndivNum"].Value);
//查找要修改的行
17 strCue = "修改"; //描述修改操作
18 }
19 else //若是添加操作状态
20 {
21 dr = dt.NewRow(); //创建新行
22 dt.Rows.Add(dr); //在数据源中添加新创建的行
23 strCue = "添加"; //描述添加操作
24 dr["DoFlag"] = "0"; //表示新记录,未作执行处理
25 }
26 //给数据源的各个字段赋值
27 dr["PlanTitle"] = txt_PlanTitle.Text.Trim();
28 dr["PlanKind"] = cbox_PlanKind.Text;
29 dr["ExecuteTime"] = dtp_ExecuteTime.Value;
30 dr["PlanContent"] = rtb_PlanContent.Text;
31 OleDbCommandBuilder scb = new OleDbCommandBuilder(oleDa); //关联数据库表单命令
32 if (oleDa.Update(dt) > 0) //若提交数据成功
33 {
34 MessageBox.Show(strCue + "成功!"); //弹出提交成功的提示框
35 }
36 else //若提交数据失败
37 {
38 MessageBox.Show(strCue + "失败!"); //弹出提交失败的提示框
39 }
40 RestUI(); //重置界面上的控件
41 ActivationControl(false); //禁用输入信息的控件
·14·
CC#典型模块精解
Note
42 dt.Clear(); //清空数据表
43 oleDa.Fill(dt); //重新填充数据表,更新 IndivNum 列
44 }
1.5.6 删除计划任务
在当前界面左侧的 DataGridView 控件中选择要删除的记录,然后单击“删除”按钮,这
时程序将弹出“确定要删除吗?”提示对话框,单击“是”按钮,即可删除当前选中的记录。
“删除”按钮的 Click 事件代码如下:
01 private void button4_Click(object sender, EventArgs e)
02 {
03 if (dgv_PlanRegister.CurrentRow != null) //若当前行不为空
04 {
05 if (MessageBox.Show("确定要删除吗?", "软件提示", MessageBoxButtons.YesNo,
MessageBoxIcon.Exclamation) == DialogResult.Yes) //若确定要删除
06 {
07 DataTable dt = dgv_PlanRegister.DataSource as DataTable; //获取数据源
08 oleDa.FillSchema(dt, SchemaType.Mapped); //配置指定的数据架构
09 //获取计划的唯一编号
10 int intIndivNum = Convert.ToInt32(dgv_PlanRegister.CurrentRow.Cells["IndivNum"]
.Value);
11 DataRow dr = dt.Rows.Find(intIndivNum); //查找指定数据行
12 dr.Delete(); //删除数据行
13 //关联数据库表单命令
14 OleDbCommandBuilder scb = new OleDbCommandBuilder(oleDa);
15 try
16 {
17 if (oleDa.Update(dt) > 0) //若提交删除命令成功
18 {
19 if (oleConn.State != ConnectionState.Open) //若数据库连接未打开
20 {
21 oleConn.Open(); //打开连接
22 }
23 MessageBox.Show("删除成功!");
24 }
25 else //若提交删除命令失败
26 {
27 MessageBox.Show("删除失败!");
28 }
29 }
30 catch (Exception ex) //处理异常
31 {
32 MessageBox.Show(ex.Message, "软件提示"); //弹出异常信息提示框
33 }
34 finally //finally 语句
35 {
36 if (oleConn.State == ConnectionState.Open) //若连接打开
37 {
38 oleConn.Close(); //关闭连接
39 }
·15·
Note
第 1 章 365 桌面提醒模块
40 }
41 }
42 }
43 }
1.6 计 划 查 询
1.6.1 计划查询功能概述
查询计划任务有两种方式,既可以按照提前天数查询将要执行的计划任务,也可以按照计
划内容(输入“计划内容”的若干关键字即可)查询相关的计划任务,这两种查询方式只能选
择其一。选择其中一种查询方式,然后单击“查询”按钮,查询出的结果将显示在当前界面右
侧的 DataGridView 控件中。计划查询界面的运行效果如图 1.14 所示。
若 为 空 , 则 查 询 全 部
图 1.14 计划查询界面
1.6.2 计划查询界面设计
在 Frm_Main 窗体上部的工具栏位置添加一个 PictureBox 控件,命名为 pic_PlanSearch,用
来作为“计划查询”按钮;在该窗体的下部添加一个 Panel 控件,命名为 panel_PlanSearch,并
在该 Panel 控件中添加若干控件,用来选择查询方式和输入查询关键字。该 Panel 控件中添加
的主要控件如表 1.3 所示。
表 1.3 计划查询界面用到的控件及说明
控 件 类 型 控件 ID 主要属性设置 用 途
txt_QueryDays 系统默认 输入提前天数
txt_PlanContent 系统默认 输入计划内容的关键字
chb_Days
Checked属性设置为true,Text属性设置为
“按照提前天数查询” 按照提前天数进行查询
chb_PlanContent Text属性设置为“按照计划内容查询” 按照计划内容查询
·16·
CC#典型模块精解
Note
续表
控 件 类 型 控件 ID 主要属性设置 用 途
dgv_PlanSearch 在Columns属性集合中添加若干项(详见源
代码);Modifiers属性设置为public 显示计划任务信息
button6 Text属性设置为“查询” 实现查询数据的操作
button7 Text属性设置为“取消” 清空界面上的文本框
1.6.3 打开计划查询界面
在窗体的工具栏中单击“计划查询”按钮,程序将设置 panel_PlanSearch 控件为可见状态,
而设置其他界面的 Panel 控件为不可见状态。“计划查询”按钮的 Click 事件代码如下:
01 private void pic_BirSearch_Click(object sender, EventArgs e)
02 {
03 //计划查询界面可见,其他界面不可见
04 panel_PlanRegister.Visible = false;
05 panel_PlanStat.Visible = false;
06 panel_CueSetting.Visible = false;
07 panel_HisSearch.Visible = false;
08 panel_PlanSearch.Visible = true;
09 //DataGridView 控件中的“是否按期执行”列绑定 listSource 数据源
10 DoFlag1.ConvertValueToText("DataValue", "DisplayText", listSource);
11 chb_Days.Checked = true; //默认按照提前天数进行查询
12 txt_PlanContent.Text = string.Empty; //清空“内容关键字”文本框
13 //创建 OleDbDataAdapter 实例,用于查询提醒设置信息
14 OleDbDataAdapter oleDa = new OleDbDataAdapter("Select Days from tb_CueSetting",
oleConn);
15 DataTable dt = new DataTable(); //创建 DataTable 实例,用于存储数据
16 oleDa.Fill(dt); //把数据填充到 DataTable 实例
17 txt_QueryDays.Text = Convert.ToString(dt.Rows[0][0]); //显示系统设置的提前天数
18 button6_Click(sender, e); //执行“查询”按钮的 Click 事件代码
19 }
1.6.4 查询计划信息
在计划查询界面上选择一种查询方式,并输入要查询的关键字,然后单击“查询”按钮,
查询的结果就会显示在当前界面左侧的 DataGridView 控件中。“查询”按钮的 Click 事件代码
如下:
01 private void button6_Click(object sender, EventArgs e)
02 {
03 //加载 SQL 语句创建 StringBuilder 实例
04 StringBuilder sb = new StringBuilder(" Select * from tb_Plan Where ");
05 if (chb_Days.Checked) //若选择按提前天数查询
06 {
07 if (String.IsNullOrEmpty(txt_QueryDays.Text.Trim())) //若天数为空
08 {
09 MessageBox.Show("天数不许为空!","软件提示"); //提示天数不许为空
·17·
Note
第 1 章 365 桌面提醒模块
10 return;
11 }
12 //过滤提前天数符合查询条件的数据
13 string strSql = "(format(ExecuteTime,'yyyy-mm-dd') >= '" + DateTime.Today.ToString
("yyyy-MM-dd") + "' and format(ExecuteTime,'yyyy-mm-dd') <= '" + DateTime.Today.AddDays (Convert.ToInt32
(txt_QueryDays.Text)).ToString("yyyy-MM-dd") + "')";
14 sb.Append(strSql); //连接查询字符串
15 }
16 else //若是按照“计划内容”查询
17 {
18 //过滤符合查询条件的计划任务
19 string strContentSql = " PlanContent like '%" + txt_PlanContent.Text.Trim() + "%'";
20 sb.Append(strContentSql); //连接查询字符串
21 }
22 oleDa = new OleDbDataAdapter(sb.ToString(), oleConn); //创建OleDbDataAdapter实例
23 DataTable dt = new DataTable(); //创建 DataTable 实例
24 oleDa.Fill(dt); //把数据填充到 DataTable 实例中
25 dgv_PlanSearch.DataSource = dt; //在 DataGridView 控件中显示数据
26 dgv_PlanSearch.AlternateColor(Color.LightYellow); //隔行换色显示数据记录
27 }
1.6.5 处理计划
在当前界面左侧的 DataGridView 控件中双击某条记录,将打开如图 1.15 所示的“处理计
划”窗口。在该窗口中可以添加或修改处理信息。若该计划已经按期完成,则需要打上“处理
标记”,并对计划的执行进行简单的说明。
图 1.15 “处理计划”窗口
如图 1.15 所示,若按期完成当前计划,则标记“该计划按期执行完毕”,并输入简短的执
行说明;若未按期完成当前计划或因其他原因取消了计划,则不用标记“该计划按期执行完毕”,
并可作简短的说明。最后单击“保存”按钮,即可保存处理信息,“保存”按钮的 Click 事件代
码如下:
01 private void button1_Click(object sender, EventArgs e)
02 {
03 string strDoFlag = String.Empty; //定义描述计划执行的标记
04 if (chb_DoFlag.CheckState == CheckState.Checked) //若标记该计划已经按期执行
05 {
06 strDoFlag = "1"; //设置计划执行标记为 1
07 }
·18·
CC#典型模块精解
Note
08 else //若标记该计划未按期执行或取消
09 {
10 strDoFlag = "0"; //设置计划执行标记为 0
11 }
12
13 string strSql = "Update tb_Plan set DoFlag = '" + strDoFlag + "',Explain='" +
rtb_Explain.Text + "' where IndivNum = " + intIndivNum; //修改处理信息
14
15 OleDbCommand oleCmd = new OleDbCommand(strSql,oleConn); //创建命令对象
16
17 if (oleConn.State != ConnectionState.Open) //若连接未打开
18 {
19 oleConn.Open(); //打开连接
20 }
21 if (oleCmd.ExecuteNonQuery() > 0) //执行 SQL 语句
22 {
23 MessageBox.Show("完成!","软件提示"); //提示完成
24 }
25 else
26 {
27 MessageBox.Show("失败!","软件提示"); //提示还未完成
28 }
29 oleConn.Close(); //关闭数据库连接
30 this.Close(); //关闭当前窗体
31 }
1.7 计 划 统 计
1.7.1 计划统计功能概述
计划统计用于查询本年度的计划执行情况,可以查询“已按期执行的计划”或“未按期执
行的计划”。计划统计界面的运行效果如图 1.16 所示。
图 1.16 计划统计界面
·19·
Note
第 1 章 365 桌面提醒模块
1.7.2 计划统计界面设计
在 Frm_Main 窗体上部的工具栏位置添加一个 PictureBox 控件,命名为 pic_PlanStat,用来
作为“计划统计”按钮;在该窗体的下部添加一个 Panel 控件,命名为 panel_PlanStat,并在该
Panel 控件中添加若干控件,用来选择统计方式和实现查询操作。该 Panel 控件中添加的主要控
件如表 1.4 所示。
表 1.4 计划统计界面用到的控件及说明
控 件 类 型 控件 ID 主要属性设置 用 途
rb_DoFlag Checked属性设置为true 表示统计“已按期执行的计划”
rb_UnDoFlag 系统默认 表示统计“未按期执行的计划”
dgv_PlanStat ShowCellToolTips属性设置为false 显示计划任务信息
button5 Text属性设置为“查询” 实现查询数据的操作
1.7.3 统计计划信息
在计划统计界面上选中“已按期执行的计划”或“未按期执行的计划”单选按钮,然后单
击“查询”按钮,即可查询相应的数据记录,并将查询结果显示在界面左侧的 DataGridView
控件中。“查询”按钮的 Click 事件代码如下:
01 private void button5_Click(object sender, EventArgs e)
02 {
03 string strSql = string.Empty; //定义存储 SQL 语句的字符串变量
04 if (rb_DoFlag.Checked) //若选择“已按期执行的计划”
05 {
06 strSql = " SELECT * FROM tb_Plan where DoFlag = '1'";//查询“已按期执行的计划”
07 }
08 else //若选择“未按期执行的计划”
09 {
10 strSql = " SELECT * FROM tb_Plan where DoFlag = '0'";//查询“未按期执行的计划”
11 }
12 oleDa = new OleDbDataAdapter(strSql, oleConn); //创建 OleDbDataAdapter 实例
13 DataTable dt = new DataTable(); //创建 DataTable 实例
14 oleDa.Fill(dt); //把数据流添加到DataTable实例中
15 dgv_PlanStat.DataSource = dt; //DataGridView 控件显示计划信息
16 dgv_PlanStat.AlternateColor(Color.LightYellow); //在 DataGridView 控件中隔行换色
显示记录
17 }
1.8 定 时 关 机
1.8.1 定时关机功能概述
定时关机功能根据事先设置的相关参数(如关机时间、关机类型、执行周期和是否启用定
·20·
CC#典型模块精解
Note
时关机功能)来决定关机操作。若执行周期设置为“每天”,则本程序每天都会定时关闭(也
可以是重启或注销)计算机;若执行周期设置为“每周几”,则本程序会在固定的星期几定时
关闭计算机。“定时关机”界面的运行效果如图 1.17 所示。
启 用 后 , 程 序 将 执 行 定 时 关 机 功 能
图 1.17 “定时关机”界面
1.8.2 定时关机界面设计
在当前项目中添加一个 Form 窗体,命名为 Frm_ClosePC,设置其 MaximizeBox 属性和
MinimizeBox 属性值均为 false,设置 StartPosition 属性值为 CenterScreen。该窗体中添加的主要
控件如表 1.5 所示。
表 1.5 “定时关机”界面用到的控件及说明
控 件 类 型 控件 ID 主要属性设置 用 途
dateTimePicker1 Format属性设置为Time 设置定时关机时间
txtMessage 无 输入提示信息
chbAutoRun 无 开机是否自动运行
cbbWeek DropDownStyle属性设置为DropDownList 选择周几执行
rbShutDown Checked属性设置为True 选择执行关机
rbBegin 无 选择执行重启
rbLogout 无 选择执行注销
rbShowMessage 无 选择显示提示信息
rbcycleDay 无 选择每天执行
rbcycleWeek 无 选择每周执行
1.8.3 保存定时关机参数
在“定时关机”界面中设置完定时关机参数之后,单击“确定”按钮,即可保存数据。程
序在保存数据之前,首先需要判断数据表(即表 SetSystem)中是否存在“定时关机”数据,
·21·
Note
第 1 章 365 桌面提醒模块
若不存在,则需要添加定时关机数据(即执行 INSERT 语句);若存在,则需要修改定时关机
数据(即执行 UPDATE 语句)。“确定”按钮的 Click 事件代码如下:
01 bool judge = true; //判断是否存在定时关机数据
02 private void AddCommand()
03 {
04 string settime1; //保存定时关机时间
05 int settype1=0; //保存关机类型
06 int autorun1 = 0; //保存是否启用定时关机功能
07 string message1="请输入提示信息";
08 int cycle1; //保存执行周期
09 settime1 = dateTimePicker1.Text; //获取定时关机时间
10 if (rbShutDown.Checked) //若选择“关机”
11 {
12 settype1 = 0; //设置类型参数值
13 }
14 if (rbBegin.Checked) //若选择“重启”
15 {
16 settype1 = 1;
17 }
18 if (rbLogout.Checked) //若选择“注销”
19 {
20 settype1 = 2;
21 }
22 if (rbShowMessage.Checked) //若选择“显示提示信息”
23 {
24 settype1 = 3;
25 }
26
27 if (chbAutoRun.Checked) //若选择“启用按预设时间关机功能”
28 {
29 autorun1 = 1; //设置参数值为 1
30
31 }
32 else //若不选择“启用按预设时间关机功能”
33 {
34 autorun1 = 0;
35
36 }
37
38 if (rbShowMessage.Checked) //若选择显示信息
39 {
40 if (txtMessage.Text == "") //若输入的信息为空
41 {
42 MessageBox.Show(" 输入提示信息! ", " 警告 ", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
43 }
44 else //若输入的信息不为空
45 {
46 message1 = txtMessage.Text.Trim(); //获取输入的显示消息
47 }
48 }
·22·
CC#典型模块精解
Note
49 if (rbcycleDay.Checked) //若选择每天执行
50 {
51 cycle1 = 0; //设置参数值为 0
52 }
53 else //若选择每周几执行
54 {
55 cycle1 = cbbWeek.SelectedIndex + 1; //设置参数值
56 }
57 //创建数据库连接
58 conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data source=" + strg);
59 conn.Open(); //打开数据库连接
60 string strSQL=""; //定义 SQL 语句字符串
61 if(judge) //若修改定时关机信息
62 {
63 strSQL="update SetSystem set SetTime='" + settime1 + "',SetType='" + settype1 +
"',IsAutoRun='" + autorun1 + "',Message='" + message1 + "',cycle='"+cycle1+"' where ID=1"; //修改定时关
机信息的 SQL 语句
64 }
65 else //若添加定时关机信息
66 {
67 strSQL = "insert into SetSystem(ID,SetTime,SetType,IsAutoRun,Message,cycle)
values (1,'" + settime1 + "','" + settype1 + "','" + autorun1 + "','" + message1 + "','" + cycle1 + "')";
//添加定时关机信息的 SQL 语句
68 }
69 cmd = new OleDbCommand(strSQL, conn); //创建命令对象
70 int k = cmd.ExecuteNonQuery(); //执行 SQL 语句
71 if (k > 0) //若执行成功
72 {
73 if (MessageBox.Show("设置成功") == DialogResult.OK)
74 {
75 conn.Close(); //关闭连接
76 this.Close(); //关闭当前窗体
77 }
78 }
79 }
上述代码中,第 61 行的成员变量 judge 表示判断在 SetSystem 数据表中是否存在定时关机数
据。该变量的值是在 JudgeID 方法中被设置的,这是一个无参、无返回值的方法。其代码如下:
01 private void JudgeID()
02 {
03 //创建数据库连接
04 conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data source=" + strg);
05 conn.Open(); //打开数据库连接
06 //创建查询是否存在数据记录的命令对象
07 cmd = new OleDbCommand("select count(*) from SetSystem where ID=1", conn);
08 int i = (int)cmd.ExecuteScalar(); //执行包含聚合函数的 SQL 语句
09 if (i == 0) //若无数据记录
10 {
11 judge = false; //judge 变量值为 false
12 }
13 else //若有数据记录
·23·
Note
第 1 章 365 桌面提醒模块
14 {
15 judge = true; //judge 变量值为 true
16 }
17 }
1.8.4 执行定时关机命令
执行定时关机命令是通过触发主窗体(即 Frm_Main 窗体)上的 timer2 控件的 Tick 事件
来实现的,在该事件的代码中主要通过调用 ExecuteCommand 自定义方法来执行关机命令(包
括关机、重启、注销和显示提示信息等命令),该方法是一个无参、无返回值的方法。其代码
如下:
01 private void ExecuteCommand()
02 {
03 refurbishInfo(); //刷新系统数据
04 string setTime = settime; //得到设置时间
05 string nowTime = DateTime.Now.ToString("hh:mm:ss"); //获取当前时间
06 if (setTime.Equals(nowTime)) //判断当前时间是否与设定时间相等
07 {
08 switch (settype) //判断命令类型
09 {
10 case 0: Shutdown(); break; //关机命令
11 case 1: BeginPC(); break; //重启命令
12 case 2: logout(); break; //注销命令
13 //弹出信息提示框
14 case 3: MessageBox.Show(message, " 提 示 ", MessageBoxButtons.OK,
MessageBoxIcon.Asterisk);
15 break;
16 }
17 }
18 }
自定义方法 refurbishInfo、Shutdown、BeginPC 和 logout 分别用来刷新定时关机参数、关
闭计算机、重新启动计算机和注销计算机。具体代码如下:
01 string settime; //保存定时时间
02 int settype; //保存关机类型
03 int autorun; //是否启用定时关机功能
04 string message; //存储提示信息
05 int cycle; //存储执行周期
06 bool judge = true; //作为是否启用自动关机功能的标记
07 //刷新定时关机参数
08 private void refurbishInfo()
09 {
10 if (oleConn.State != ConnectionState.Open) //若数据库连接未打开
11 {
12 oleConn.Open(); //打开数据库连接
13 }
14 //创建命令对象
15 OleDbCommand cmd = new OleDbCommand("select * from SetSystem where ID=1",
oleConn);
·24·
CC#典型模块精解
Note
16 OleDbDataReader dr = cmd.ExecuteReader(); //得到只读数据流
17 dr.Read();
18 settime = Convert.ToDateTime(dr["SetTime"]).ToString("hh:mm:ss");//获取设置的关机时间
19 settype = Convert.ToInt32(dr["SetType"]); //获取关机类型
20 autorun = Convert.ToInt32(dr["IsAutoRun"]); //获取是否开机运行
21 message = dr["Message"].ToString(); //获取提示信息
22 cycle = Convert.ToInt32(dr["cycle"]); //获取执行周期
23 dr.Close(); //关闭只读数据流
24 oleConn.Close(); //关闭数据库连接
25 }
26 //关闭计算机
27 private void Shutdown()
28 {
29 DoExitWin(EWX_SHUTDOWN); //调用 DoExitWin()函数实现关机
30 Application.Exit(); //退出应用程序
31 }
32 //重启计算机
33 private void BeginPC()
34 {
35 DoExitWin(EWX_REBOOT); //调用 DoExitWin()函数实现重启
36 Application.Exit(); //退出应用程序
37 }
38 //注销计算机
39 private void logout()
40 {
41 ExitWindowsEx(EWX_LOGOFF, 0); //调用 ExitWindowsEx()函数实现注销
42 Application.Exit(); //退出应用程序
43 }
上述代码中用到了 API 函数 ExitWindowsEx(),该函数实现退出 Windows 功能,并用特定
的选项重新启动;另外,还调用了自定义方法 DoExitWin,该方法封装了 ExitWindowsEx()函数,
实现按照指定的操作执行 ExitWindowsEx()函数。其代码如下:
01 private bool DoExitWin( int flg )
02 {
03 bool ok; //声明保存返回值的变量
04 ok = ExitWindowsEx( flg, 0 ); //根据参数执行 ExitWindowsEx()函数
05 return ok; //返回方法的返回值
06 }
注意:
ExitWindowsEx()函数调用后会立刻返回,系统关闭过程是在后台进行的。注意先中止自己
的应用程序,使关闭过程更显平顺。当然,必须有足够的优先权,否则也不能执行这种操作。
·25·
Note
第 1 章 365 桌面提醒模块
1.9 技 术 提 炼
1.9.1 通过修改注册表实现自动运行程序
为了在托盘菜单中设置开机自动运行(或取消开机自动运行)的功能,本软件通过修改注
册表控制程序开机自动运行,即通过设置注册表中“HKEY_ LOCAL_MACHINE\SOFTWARE\
Microsoft\Windows\CurrentVersion\Run”的键值来实现的。
本软件对注册表进行操作时,主要使用了 RegistryKey 类的 OpenSubKey 方法、
CreateSubKey 方法、GetValue 方法、SetValue 方法和 DeleteValue 方法。下面对这 5 个方法分别
进行讲解。
1.OpenSubKey 方法
该方法主要用来检索注册表中指定的子项,其语法格式如下:
public RegistryKey OpenSubKey (string name)
其中,参数 name 表示要以只读方式打开的子项的名称或路径。
返回值为请求的子项,如果操作失败,则为空引用。
2.CreateSubKey 方法
该方法主要用来创建一个新子项或打开一个现有子项以进行写访问,其语法格式如下:
public RegistryKey CreateSubKey(string subkey)
其中,参数 subkey 表示要创建或打开的子项的名称或路径。
返回值为 RegistryKey 对象,表示新建的子项或 nullNothingnullptrnull 引用。如果为 subkey
指定了零长度字符串,则返回当前的 RegistryKey 对象。
3.GetValue 方法
该方法主要用来检索与指定名称关联的值,其语法格式如下:
public Object GetValue(string name)
其中,参数 name 表示要检索的值的名称。
返回值为与 name 关联的值;如果未找到 name,则为 null。
4.SetValue 方法
该方法主要用来设置指定的名称/值对,其语法格式如下:
public void SetValue(string name,Object value)
参数说明:
name:要存储的值的名称。
value:要存储的数据。
5.DeleteValue 方法
该方法主要用来从注册表项中删除指定值,其语法格式如下:
public void DeleteValue(string name,bool throwOnMissingValue)
·26·
CC#典型模块精解
Note
参数说明:
name:要删除的值的名称。
throwOnMissingValue:指示在找不到指定值的情况下是否引发异常。如果该参数为
true,并且指定的值不存在,则引发异常;如果该参数为 false,并且指定的子项不存
在,则不执行任何操作。
例如,本软件中的自定义方法 SetStartupApplication 用于设置开机自动运行程序(或取消
自动运行程序)。具体代码如下:
01 /// 设置开机自动运行程序(或取消自动运行程序)
02 /// <param name="strAppDir">应用程序完整路径</param>
03 /// <param name="flag">启动或取消标记</param>
04 /// <param name="menuItem">菜单项</param>
05 private void SetStartupApplication(string strAppDir, StartupFlag flag,ToolStripMenuItem
menuItem)
06 {
07 string strExeFileName = strAppDir.Substring(strAppDir.LastIndexOf("\\") + 1);
//读取可执行文件的名称
08 //读取注册表指定项
09 RegistryKey RKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\
Windows\\CurrentVersion\\Run", true);
10 if (flag == StartupFlag.StartupYes) //若要设置自动运行程序
11 {
12 if (RKey == null) //若指定的子项不存在
13 //以创建的方式打开指定项
14 RKey=Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\
CurrentVersion\\Run");
15 RKey.SetValue(strExeFileName, strAppDir); //设置该子项的新“键值对”
16 }
17 else //若要取消自动运行设置
18 {
19 if (RKey != null) //若指定的子项存在
20 RKey.DeleteValue(strExeFileName, false); //删除指定“键名”的键
21 }
22 menuItem.Checked = true; //设置当前菜单项被选中
23 }
上述代码中用到了 StartupFlag 类型的枚举值(自定义枚举),该枚举值用来作为启动或取
消自动运行程序的标记。其代码如下:
01 private enum StartupFlag
02 {
03 StartupYes = 1, //表示自动运行应用程序
04 StartupNo = 0 //表示取消自动运行应用程序
05 }
1.9.2 使用多线程处理弹出“提示气泡”任务
启动 365 桌面提醒模块后,程序需要根据设定的“提醒间隔”间歇性地向用户弹出“提示
气泡”,主动提示近期将要执行的计划信息,但这项任务会增加系统主线程的压力,因为同时
用户还可能使用主线程执行其他的任务(比如,录入计划、处理计划等)。为了减轻主线程的
·27·
Note
第 1 章 365 桌面提醒模块
工作压力,本软件中使用了多线程技术,通过创建一个新的子线程来管理向用户弹出“提示气
泡”的任务。在实现过程中,主要用到了 Thread 类的 Start 方法和 IsBackground 属性。
Thread 类位于 System.Threading 命名空间下,主要用于创建并控制线程、设置线程优先级
并获取其状态。一个进程可以创建一个或多个线程以执行与该进程关联的部分程序代码,线程
执行的程序代码由 ThreadStart 委托或 ParameterizedThreadStart 委托指定。Start 方法用来使线程
被安排执行;IsBackground 属性用于设置新的子线程在后台执行。例如,本软件在 Timer 控件
的 Tick 事件代码中使用了多线程处理技术,具体代码如下:
01 private void timer1_Tick(object sender, EventArgs e)
02 {
03 //创建一个新的子线程,用于检索和提示数据,以减轻主线程的压力
04 Thread th = new Thread(
05 () =>
06 {
07 Invoke(
08 (MethodInvoker)(() => //在当前窗体的线程上执行指定的委托
09 {
10 int intDays; //存储提前天数
11 OleDbDataAdapter oleDa = new OleDbDataAdapter("Select Days
from tb_CueSetting", oleConn); //创建实例,读取提前天数
12 DataTable dt = new DataTable(); //创建 DataTable 实例
13 oleDa.Fill(dt); //把数据写入 DataTable 实例中
14 //获取提前天数
15 intDays = Convert.ToInt32(dt.Rows[0][0]);
16 //创建动态 SQL 字符串,读取将要执行的计划信息
17 StringBuilder sb = new StringBuilder(" Select PlanTitle from
tb_Plan Where ");
18 //过滤日期符合查询条件的记录
19 string strSql = " DoFlag = '0' and
(format(ExecuteTime,'yyyy-mm-dd') >= '" + DateTime.Today.ToString("yyyy-MM-dd") + "' and
format(ExecuteTime,'yyyy-mm-dd') <= '" + DateTime.Today.AddDays(intDays).ToString("yyyy-MM-dd") +
"')";
20 sb.Append(strSql); //追加字符串
21 //得到新的 OleDbDataAdapter 实例
22 oleDa = new OleDbDataAdapter(sb.ToString(), oleConn);
23 oleDa.Fill(dt); //把数据写入 DataTable 实例中
24 sb.Clear(); //清空动态字符串
25 foreach (DataRow dr in dt.Rows)//遍历数据行,追加多条计划信息
26 {
27 //追加字符串(包括一个换行符)
28 sb.Append(dr["PlanTitle"].ToString() + Environment.NewLine);
29 }
30 //若存在将要执行的计划
31 if (!String.IsNullOrEmpty(sb.ToString().Trim()))
32 {
33 string strTemp = string.Empty; //定义存储提示信息的
字符串变量
34 if (intDays == 0) //若是当天内
35 {
36 strTemp = "今天有以下未执行的计划任务:";
·28·
CC#典型模块精解
Note
37 }
38 else //若不是当天,而是以后几天内
39 {
40 strTemp = "未来" + intDays + "天内有以下未执行的
计划任务:";
41 }
42 this.notifyIcon1.ShowBalloonTip(1000, " 计 划 提 示 :
",strTemp + sb.ToString() + "详细情况请单击托盘图标!", ToolTipIcon.Info); //弹出提示气泡,显示 1 秒钟
43 }
44 else //若不存在将要执行的计划
45 {
46 string strTemp = string.Empty;
//定义存储提示信息的字符串变量
47 if (intDays == 0) //若是当天内
48 {
49 strTemp = "今天无未执行的计划任务:";
50 }
51 else //若是未来几天内
52 {
53 strTemp = "未来" + intDays + "天内无未执行的计划任务:";
54 }
55 this.notifyIcon1.ShowBalloonTip(1000, " 计 划 提 示 : ",
strTemp + "\n 详细情况请单击托盘图标!", ToolTipIcon.Info); //弹出提示气泡,显示 1 秒钟
56 }
57 }));
58 });
59 th.IsBackground = true; //设置新的子线程在后台执行
60 th.Start(); //启动新的子线程
61 }
1.9.3 使用扩展方法为系统类型添加新功能
本软件在进行数据绑定和设置 DataGridView 控件的隔行换色时,使用了扩展方法。其中
一个是为系统类型 DataGridViewComboBoxColumn 添加数据绑定的功能,另一个是为
DataGridView 控件添加隔行换色的功能。下面对扩展方法进行介绍。
扩展方法可以向现有类型中“添加”方法,而无须创建新的派生类型、重新编译或以其他
方式修改原始类型。扩展方法是一种特殊的静态方法,可以像实例方法一样进行调用。
要定义扩展方法,需要注意以下几点:
扩展方法必须被定义在一个静态类中,扩展方法自身必须是一个静态方法。
扩展方法中的首个参数必须是 this,后面紧跟扩展类的名称,接着是参数名称。
扩展方法可以被扩展类的对象调用,也可以使用在其中定义扩展方法的静态类直接
调用。
说明:
本软件中关于扩展方法的示例代码参见 1.3.2 小节,这里不再赘述。
·29·
Note
第 1 章 365 桌面提醒模块
1.9.4 应用初始化器创建集合或对象
本软件使用集合初始化器和对象初始化器创建一个 List<CalFlag>类型的集合对象,然后将
该集合对象绑定到 DataGridView 控件的“是否按期执行”列。下面就来对对象初始化器和集
合初始化器作一简介。
1.对象初始化器
对象初始化器允许在创建对象时使用一条语句为对象指定一个或多个属性(或公共字段)
的值,这样就可以以声明的方式初始化任意类型的对象。其语法格式如下:
【数据类型或 var】 对象名称 = new 【数据类型】 { 【属性或公共字段 1】,【属性或公共字
段 2】,„}
2.集合初始化器
集合初始化器允许在创建集合对象时使用一语句为集合对象添加若干个元素,这样就可以
以声明的方式向集合对象中添加元素并初始化元素,使 C#程序变得更加优雅和简洁。其语法
格式如下:
【集合数据类型或 var】 集合对象名称 = new 【集合数据类型】 { 【元素 1】, 【元素 2】,
【元素 3】,„}
例如,本软件中创建的 List<CalFlag>类型的集合对象如下:
01 List<CalFlag>listSource=new List<CalFlag> //listSource 作为“是否按期执行”列的数据源
02 {
03 new CalFlag{ DataValue ="1", DisplayText = "是"}, //表示“是”的元素
04 new CalFlag{ DataValue ="0", DisplayText = "否"} //表示“否”的元素
05 };
1.9.5 使用 ADO.NET 对象提交数据
在本软件中,当保存“计划录入”的数据时,使用 DataAdapter 对象的 Update 方法来提交
数据;当保存“提醒设置”和“处理计划”的数据时,使用 Command 对象的 ExecuteNonQuery
方法来提交数据。下面就来介绍这两种方法。
1.DataAdapter 对象的 Update 方法
该方法为指定的DataSet中每个已插入、已更新或已删除的行调用相应的 INSERT、UPDATE
或 DELETE 语句,其语法格式如下:
public virtual int Update(DataSet dataSet)
其中,参数 dataSet 表示用于更新数据源的 DataSet。
返回值为 DataSet 中成功更新的行数。
说明:
关于 DataAdapter 对象的 Update 方法的具体应用参见 1.5.5 小节的代码,这里不再赘述。
2.Command 对象的 ExecuteNonQuery 方法
该方法实现执行 SQL 语句,并返回受影响的行数。其语法格式如下:
·30·
CC#典型模块精解
Note
public override int ExecuteNonQuery ()
返回值为执行 SQL 语句后受影响的行数。
例如,下面的示例代码实现将企业所有员工工资上调 100 元。
01 //设置更新 SQL 语句,工资增加 100 元
02 cmd.CommandText = "update tb_laborage set 工资=工资+100;
03 //设置 CommandType 属性为 Text,使其只执行 SQL 语句文本形式
04 cmd.CommandType = CommandType.Text;
05 //使用 ExecuteNonQuery 方法执行 SQL 语句
06 cmd.ExecuteNonQuery();