C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)

欢迎大家提出意见,一起讨论!

转载请标明是引用于 http://blog.csdn.net/chenyujing1234

源码: http://www.rayfile.com/zh-cn/files/2c3f0ff5-be79-11e1-8f2f-0015c55db73d/ (采用VS2008或VS2010打开)

 

编译平台:VS2010 + .Net Framework 3.5

        语言: C#

 

3、 系统数据库设计

3、1  数据库设计

系统使用微软SQL Server或SQL Express进行后台数据存储,并且使用Entity Framework来创建实体模型。整个系统由以下4个数据表组成。

(1) Category报表: 保存任务分类信息

(2) Resource表:  保存任务资源信息

(3) Task表: 保存任务详细信息

(4) TaskResource表:保存与任务相关联的资源列表。

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第1张图片

在VS中直接创建数据库,步骤如下:

(1) 选择“添加”|“新建项”,选择“基于服务的数据库”。如下图:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第2张图片

点击添加,此时数据库被加到Database文件夹中。VS会弹出数据源配置向导,允许用户先行设计数据库模型,再根据模型产生数据库表。

在这里单击“取消”按钮,示例将使用根据数据库表来生成数据实体模型,因此先使用表设计工具创建表。

(2) 在解决方案资源管理器上双击TaskManager.mdf 文件,VS将弹出服务器资源管理器,并展开数据连接,右击表节点

在弹出的菜单中选择“添加新表”选项,VS将弹出表设计窗口。如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第3张图片

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第4张图片

 

 

在列名字段中,输入表字段名称,在数据类型下选择类型。

(3) 在创建了表后,可以利用数据库关系图创建表之间的主外键关系。

在服务器资源管理器中,展开数据连接,找到数据库关系系图节点,右击鼠标,选择“添加新关系图”选项。

在图形化的表视图上,右击要设置主键的字段,从弹出的菜单中选择“设置主键”选项,可以拖动表到要建立关系的目标表。如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第5张图片

 

服务器资源管理器还提供了视图、存储过程等节点,允许用户直接在VS中创建存储过程,不需要在开发机上安装SQLServer 版,增加开发效率。

 

3、2  使用Entity Framework生成实体类

Microsoft Entity Framework是微软基于ADO.NET技术所发展出来的对象关系映射(O/R Mapping)解决方案,早期称为ObjectSpace.

在过去,程序员总是与数据库不可分割,不可避免要使用数据库相关的SQL语句。ORM对象关系映射技术因此而生,NHibernate是.NET平台下

用于实现ORM的热门工具,微软公司不甘落后,推出了ADO.NET Entity Framework框架。

Entity Framework利用了抽象化数据结构的方式,将每个数据库对象都转成应用程序对象(entity),而数据字段都转换为属性(property),

关系则转换为结合属性(association),让数据库的E/R模型完全转成对象模型,因此能让程序员用最熟悉的编程语言来调用访问。

在抽象化的结构之下,则是高度集成与对应结构的概念层、对应层和储存层,以及支持Entity Framework的数据提供者(provider),让数据访问的工作得以顺利与

完整进行。

(1) 为了将TaskManager数据库中的表转换为实例对象模型,在解决方案资源管理器中右击Database文件夹,在弹出的菜单中选择“添加”|“新建项”命令,

选择ADO.NET实体数据模型,点添加后进入到实体数据模型向导窗口。

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第6张图片

(2) 在该窗口中选择从数据库生成模型,单击下一步按钮,将进入数据连接选择窗口。选择当前创建的数据库连接。

(3) 在指定了连接后,进入选择数据库对象窗口。如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第7张图片

这样之后我们就得到了数据库对应的实例代码文件XXXX.cs,它的开头有下面的说明

//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本:4.0.30319.1
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------


 

 

(4) 在选择完成后,VS进入edmx实体数据模型设计窗口。在该窗口中,为每一个表创建一个对象,将每个字段添加为属性,并在数据库间

        指定的表间关系创建对象关系。如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第8张图片

(5) 如果在开发中数据库结构发生变更,可以将模型与数据库间保持同步。

      在设计器上右击,选择“从数据库更新模型”选项,VS将弹出选择实体模型窗口,选择要更新的实体模型后单击“完成”。

 

4、 任务管理功能的实现

4、1 主窗口设计

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第9张图片

在ToolStrip的项集合编辑器中,将每个按钮的DisplayStyle 指定为Image,如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第10张图片

主窗口在启动后,会打开任务面板窗口中,为了重用主窗口的打开窗口代码,创建一个名为OpenChildForm()方法

主窗口的Shown事件在主窗口已经加载,在显示时发生,在该事件中通过调用OpenChildForm()方法来实现任务面板的显示。

在private void InitializeComponent()中先指定窗体是首次显示后调用的函数:

this.Shown += new System.EventHandler(this.MainForm_Shown);


 

 //主窗口显示Shown时的事件处理代码
        private void MainForm_Shown(object sender, EventArgs e)
        {
            OpenTaskForm();//使用OpenTaskForm方法打开任务窗口
        }


 

 /// <summary>
        /// o打开任务窗口辅助方法
        /// </summary>
        private void OpenTaskForm()
        {
            //判断当前程序中窗口是否己被关闭
            if (_taskForm == null || _taskForm.IsDisposed)
            {   //实例化新窗口
                _taskForm = new TaskForm();
            }
            //调用通用的OpenChildForm方法打开子窗口
            OpenChildForm(_taskForm);
        }


 

 /// <summary>
        /// 打开子窗口的通用方法代码
        /// </summary>
        /// <param name="childForm">子窗口Form实例</param>
        private void OpenChildForm(Form childForm)
        {
            childForm.MdiParent = this;//设置MDI父对象
            //指定窗口的样式为最大化
            childForm.WindowState = FormWindowState.Maximized;
            childForm.Show();//显示子窗口
        }


Shown事件处理代码中调用了OpenTaskForm() 方法,该方法判断_tastForm实例是否已释放。

主窗口为所有的子窗口都维护一个类级别的变量,如果用户重复打开一个已经存在的窗口,将不会重复多次创建。

 

4、2  任务管理窗口

左侧一个DataGridView控件组成;

右侧是一个自定义的用户控件,用来实现任务详细信息的显示;

底部是一排ComboBox控件允许用户进行选择过滤。

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第11张图片

在任务窗口显示时,Shown事件触发,在事件处理代码中完成了数据从数据库的加载工作

Shown事件代码调用了BindDropDowns()方法向DropDownList控件中填入数据,使用LaadTastList()方法向

DataGridView和TaskViwer控件中填入要显示的任务数据。

  private void TaskForm_Shown(object sender, EventArgs e)
        {
            BindDropDowns();//绑定到DropDownList控件
            LoadTaskList(); 
        }


 

 /// <summary>
        /// 向提供选择参数的下拉列表框填入数据
        /// </summary>
        private void BindDropDowns()
        {   //填充优先级下拉列表框 
            Utility.BindTaskPriorityCombo(cmbPriority, true);
            //填充任务状态下拉列表框 
            Utility.BindTaskStatusCombo(cmbStatus, true);
            //填充颜色分类下拉列表框 
            Utility.BindTaskColorCategoryCombo(cmbColorCategory, true);
            //填充任务标志下拉列表框 
            Utility.BindTaskFlagCombo(cmbFlag, true);
            //从数据表中绑定任务分类到下拉列表框
            BindCategoryDropDown();
        }


所有Utility类中的BindXXX方法都是用于将定义在公共单元中的任务参数枚举值与ComboBox控件绑定,

只有BindCatagoryDropDown实现了从数据库中加载任务分类数据的功能

 /// <summary>
        /// 绑定优先级枚举值到下拉列表框
        /// </summary>
        public static void BindTaskPriorityCombo
            (ComboBox priorityCombo, bool addEmpty)
        {
            priorityCombo.DrawMode = DrawMode.OwnerDrawVariable;//设置自绘制模式
            //DrawItem事件在绘制项时发生,通过关联该事件提供自定义的绘制方式
            priorityCombo.DrawItem += new DrawItemEventHandler(priorityCombo_DrawItem);
            priorityCombo.Items.Clear();//清空项列表
            if (addEmpty)
            {   //如果要添加空白,则添加一个空的字符串
                priorityCombo.Items.Add("");
            }
            //使用Enum.GetValues返回枚举值集合
            foreach (TaskPriority priority in Enum.GetValues
                (typeof(TaskPriority)))
            {   //向项中添加枚举值
                priorityCombo.Items.Add(priority);
            }
        }


由于数据表已经被建模为Entity 实体,因此将使用实体类来直接绑定到ComboBox控件,如下:

 /// <summary>
        /// 绑定到任务分类下拉列表框 
        /// </summary>
        private void BindCategoryDropDown()
        {  //实例化EDM模型
            TaskManagerEntities taskMgrEntity = new TaskManagerEntities();
            //使用ToList方法将ObjectQuery<Category>转换为泛型列表
            List<Category> categoryList = taskMgrEntity.Category.ToList();
            //为了在ComboBox中插入一个空白列,实例化一个新的Category
            Category newCategory = new Category();
            //将该Category插入到列表第1行
            categoryList.Insert(0, newCategory);
            //指定ComboBox的绑定到的显示成员与值成员
            cmbCategory.DisplayMember = "Name";
            cmbCategory.ValueMember = "CategoryID";
            //指定其DataSource为返回的List<Category>泛型
            cmbCategory.DataSource = categoryList;
        }


Entity Framework中的每个实例表现为System.Data.Objects.ObjectQuery<T>泛型数据,用来表示一个可以返回

特定类型的集合或多个对象的查询。

在使用ObjectQuery执行查询前,必须要实例化ObectContext,这个对象提供了连接和元数据信息,

一个对象查询在以下情形下被执行:

(1)使用循环遍历语句,比如foreach枚举集合。

(2)将该值赋给List<T>泛型集合。

(3)显示调用Execute()方法执行查询。

在BindCategoryDropDown()方法中,

首先实例化了TaskManagerEntities对象。该对象由VS自动生成,派生自global::System.Data.Objects.ObjectContext类。

然后调用ToList()方法将Category表转换为泛型列表,此时查询被执行,数据被取出。

为了在ComboBox 中的第一行显示一个空白行,实例化一个新的Category类,插入到列表的第一行。

 

4、3  加载任务列表

LoadTaskList()方法用于将数据绑定到DataGridView 控件,同时根据DataGridView 控件首行数据填充TaskViewer控件显示详细的任务信息。

LoadTaskList() 代码如下:

/// <summary>
        /// 加载任务列表
        /// </summary>
        public void LoadTaskList()
        {    //实例化ObjectContext的派生类
            TaskManagerEntities taskMgrEntity = new TaskManagerEntities();
            string whereClause = GetWhereClause();//获取查询参数
            //取消自动生成列
            gvTasks.AutoGenerateColumns = false;
            //如果查询条件为空,表示查询所有值
            if (string.IsNullOrEmpty(whereClause))
            {   //查询Task表,并在导航属性中包含Category表
                gvTasks.DataSource = taskMgrEntity.Task.Include("Category");
            }
            else
            {   //查询Task表,并在导航属性中包含Category和Resource表
                gvTasks.DataSource = 
                    taskMgrEntity.Task.Include("Category").
                    Include("Resource").Where(whereClause);
            }
            ///格式化网格行
            FormatGridRow();
            //如果当前行大于0
            if (gvTasks.Rows.Count > 0)
            {   //选中DataGridView的第1行
                gvTasks.Rows[0].Selected = true;
                LoadSelectedTaskDetail();//填充TaskViewer控件
            }
        }


LoadTaskList() 方法将要从数据库中同时拿到多个表的数据,主要数据表Task ,以及与主数据表关联的Category和Resource表。在代码中,首先实例化
TaskManagerEntities这个ObjectContext对象,然后使用GetWhereClause构造LING的where查询语句

为了让界面显示效果美观,将AutoGenerateColumns属性设置为false手动在项集合编辑器中设置列有显示外观。

如果没有过滤条件,那么将取出所有的Task数据,通过使用ObejctQuery的Include拿出任务关联的Category表中的数据。

如果用户选择了要过滤的参数,比如任务类别或者是通过资源来过滤,那么Task将会包含Category与Resource表,并使用where进行查询。

用户将gvTasks.DataSource属性设置为查询的结果后,DataGridView将显示任务列表数据

 这是由于在GridView的设计中为各列绑定了数据:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第12张图片

 

任务面板使用FormatGridRow()方法时一步地格式化了DataGridView中的行,如果当前加载的数据行大于0,

那么代码将选中第一行。最后调用LoadSelectedTaskDetail填充TaskViewer控件,用来显示任务详细信息。

GetWhereClause()方法拼合用户下拉列表框中选择的条件下,将这些组合为LING的where语句。代码如下:

/// <summary>
        /// 根据控件的选择生成where语句
        /// </summary>
        private string GetWhereClause()
        {
            string whereClause = "";
            //如果选择了优先级选项
            if (cmbPriority.SelectedIndex > 0)
            {   //使用ConcatWhereClause连接操作符(AND/OR)与新语句
                whereClause = ConcatWhereClause(whereClause, "it.Priority = '" + 
                    cmbPriority.Text + "'", string.Empty);
            }
            //如果用户选择了要过滤的状态
            if (cmbStatus.SelectedIndex > 0)
            {   //查询Status,并连接AND运行符
                whereClause = ConcatWhereClause(whereClause, 
                    "it.Status = '" + cmbStatus.Text + "'", "AND");
            }
            //如果用户选择了任务分类
            if (cmbCategory.SelectedIndex > 0)
            {  //使用Task.Category.CategoryID来过滤任务
                whereClause = ConcatWhereClause(whereClause, 
                    "it.Category.CategoryID = " 
                    + cmbCategory.SelectedValue.ToString(), "AND");
            }
            //如果用户选择了颜色分类下拉列表框
            if (cmbColorCategory.SelectedIndex > 0)
            {  //查询ColorCategory属性的值
                whereClause = ConcatWhereClause(whereClause, 
                    "it.ColorCategory = '" 
                    + cmbColorCategory.Text + "'", "AND");
            }
            //如果用户选择了标志下拉列表框 
            if (cmbFlag.SelectedIndex > 0)
            {  //查询Flag
                whereClause = ConcatWhereClause(whereClause,
                    "it.Flag = '" + cmbFlag.Text + "'", "AND");
            }
            //返回where语句
            return whereClause;
        }

得到的串比如如下:


在Entity Framework中,指定it表示查询源自身,即Task数据源。在GetWhereClause()方法中,总是判断

ComboBox控件中的SelectedIndex属性是否选择了非0值。然后调用ConcatWhereClause() 方法,该方法接收3个参数:

要合并的where语句,然后是条件、值,最后一个参数表示如查是多条件的话,使用哪一种条件运算符,比如AND 或 OR语句。

 

4、4  格式化DataGridView控件

FormatGridRow来来格式化DataGridView的行样式。这个方法的存在是为了让不同状态的Task可以具有不一样的显示效果,以便用户可以一眼分辨任务的状态。

 /// <summary>
        /// 格式化网格行
        /// </summary>
        private void FormatGridRow()
        {   //循环遍历DataGridView的行集合
            foreach (DataGridViewRow gridRow in gvTasks.Rows)
            {   //通过DataGridViewRow绑定的DataBoundItem对象得到Task
                Task task = gridRow.DataBoundItem as Task;
                //获取Task状态字符串
                String stringValue = task.Status;
                if (!string.IsNullOrEmpty(stringValue))
                {  //将字符串类型的状态转换为枚举值
                    TaskStatus status = (TaskStatus)Enum.Parse
                        (typeof(TaskStatus), stringValue);
                    //如果当前任务的状态标记为己完成
                    if (status == TaskStatus.Completed)
                    {
                        //设置DataGridViewRow行的默认样式
                        gridRow.DefaultCellStyle.Font = 
                            new Font(gvTasks.RowsDefaultCellStyle.Font, 
                                FontStyle.Strikeout);
                        //指定行的前景色为亮灰色
                        gridRow.DefaultCellStyle.ForeColor = Color.LightGray;
                    }
                    else
                    {
                        //指定其他样式
                    }
                }
            }
        }


效果如下图:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第13张图片

DataGridView控件的每一行是一个DataGridViewRow对象,当将DataGridView的DataSource设置为一个对象集合时,

DataGridViewRow的DataBoundItem就是这个集合中的单一的对象。比如将DataGridView绑定到DataSet的某个DataTable数据表上的话,

DataBoundItem就是DataRow对象实例。

 

4、5  加载所选的Task详细信息

LoadSelectedTaskDetail()方法将使用Task对象中的属性来填充在TaskViewer用户控件中的控件,

TaskViewer控件接收一个Task对象来填充自身。因此当选中每一行时,LoadSelectedTaskDetail()方法将从DataGridViewRow的DataBoundItem

中找Task对象,传递给在用户控件中定义的SetTaskDetail定义控件的呈现。代码如下:

  /// <summary>
        /// 根据当前选择的行加载任务详细信息面板
        /// </summary>
        private void LoadSelectedTaskDetail()
        {   //用户必须选中一行才能够显示任务详细
            if (gvTasks.SelectedRows != null && 
                gvTasks.SelectedRows.Count > 0)
            {   //从DataBoundItem中获取Task对象实例
                Task task = gvTasks.SelectedRows[0].DataBoundItem as Task;
                //调用SetTaskDetail方法显示任务详细信息
                taskViewer1.SetTaskDetail(task);
            }
        }


代码中首先判断DataGridView的SelectedRows是否不为null,即是否选中了值,并且DataGridView控件的SelectedRows集合的总数要大于0。

如果条件成立,将从SelectedRews[0]这个DataGridViewRow对象中获取DataBoundItem对象,强制转换为Task类型,

调用TaskViewer 控件的SetTaskDetail方法传入该Task进行显示

 

4、6  使用CellFormatting事件格式化单元格

在DataGridView控件中定义了几个DataGridViewImageColume类型的列,比如第一行的任务优先级列、指定颜色分类的ColorCatagory的表以及指定标志的

Flag属性列。

在运行时将使用Task相应的属性来设置DataGridViewImageColumn中要显示的图像。

DataGridView控件的集合编辑器如下图:

 

 

为了能在图像列中显示定制的图像,DataGridView的CellFormating事件在需要对单元格进行格式化时产生,通过响应该事件,

可以定制DataGridView单元格的外观。其事件如下:

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第14张图片

 

   //定制单元格的显示格式,使单元格能显示出图像
        private void gvTasks_CellFormatting
            (object sender, DataGridViewCellFormattingEventArgs e)
        {  //得到当前要进行格式化设置的列
            DataGridViewColumn column = gvTasks.Columns[e.ColumnIndex];
            //如果列表为Priority
            if (column.Name.Equals("Priority"))
            {   //调用该方法为任务优先级列设置图像
                FormatPriorityCell(e);
            }
            else if (column.Name.Equals("ColorCategory"))
            {   //调用该方法为颜色分类列设置图像
                FormatColorCell(e);
            }
            else if (column.Name.Equals("TaskFlags"))
            {  //调用该方法用来设置任务标志图像
                FormatFlagCell(e);
            }
        }


CellFormatting事件的DataGridViewCellFormattingEventArgs类型的参数e包含了要格式化的单元格的一些信息。

比如当前单元格的行或列的索引值,分别是ColumnIndex和RowIndex属性。

CellStyle用来设置当前单元格的样式。如果单元格成功格式化,可以设置FormattingApplied布尔属性。

在代码中,通过e.ColumnIndex的列索引返回DataGridViewColumn对象。通过比较列名称字符串,对于Priority、ColorCateGory和

TaskFlags这3个列,调用Formatxxx()方法进行图像的设置。这3列的值其实是枚举类型,因此这几个方法主要根据在列值转换后的

枚举类型的值,然后从资源中获取图像列要显示的图像。以FormatPriorityCell()方法为例:

 /// <summary>
        /// 格式化优先级单元格
        /// </summary>
        private void FormatPriorityCell(DataGridViewCellFormattingEventArgs e)
        {
            // 确保返回值为一个字符串类型
            String stringValue = e.Value as string;
            if (stringValue == null) return;//如果值为空则返回
            // 得到当前要设置图像的单元格
            DataGridViewCell cell = gvTasks[e.ColumnIndex, e.RowIndex];
            //设置单元格的提示信息为字符值
            cell.ToolTipText = stringValue;
            //使用图像替换掉单元格字符串值
            switch (stringValue.ToLower())
            {
                case "high"://从资源中取出最高优先级的Bitmap对象
                    e.Value = Properties.Resources.High_Priority;
                    break;
                case "low"://从资源中取出最高低先级的Bitmap对象
                    e.Value = Properties.Resources.Low_Priority;
                    break;
                case "medium"://从资源中取出中等优先级的Bitmap对象
                    e.Value = Properties.Resources.Medium_Priority;
                    break;
                default://默认情况下,显示一个空白的图像
                    e.Value = Properties.Resources.Empty;
                    break;
            }
        }


代码从DataGridViewCellFormattingEventArgs类型参数e中获取单元格当前的值,并转换为字符串形式以便进行判断。

然后将单元格的值设置为DataGridViewCell的ToolTipText 属性,在鼠标停在单元格上时,能在提示信息中显示单元格的值。

  4、7  显示任务详细信息

如果用户单击DataGridView控件上的单元格时,会触发CellClick事件,在救命通过判断用户是否单击了编辑列来显示一个添加任务窗口。

CellClick事件处理如下:

  private void gvTasks_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0 && e.ColumnIndex >= 0)//如果行列都在数据范围内
            {   //得到当前单击的列
                DataGridViewColumn column = gvTasks.Columns[e.ColumnIndex];
                if (column.Name == "EditTask")//判断列名是否为EditTask
                {   //从DataBoundItem中获取Task对象
                    Task task = gvTasks.Rows[e.RowIndex].DataBoundItem as Task;
                    if (task != null)
                    {   //调用ShowAddTaskForm方法传入任务ID显示添加或编辑任务窗口
                        ShowAddTaskForm(task.TaskID);
                    }
                }
            }
        }


CellClick的DataGridViewCellEventArgs类型的参数包含当前单击的行列索引,

通过使用用列索引来获取当前单击的列,判断列的名称是否是要进行处理的列;

然后使用RowIndex得到当前单击的行,通过DataGridViewRow的DataBoundItem属性得到绑定的Task对象实例;

最后调用ShowAddTaskForm()方法传入一个TaskID以便窗口显示当前单击的Task对象实例供用户编辑。

4、8  添加/编辑任务窗口

当用户在任务窗口中单击编辑按钮会进入AddTaskForm窗口,或者用户从工具栏中单击“添加新任务”按钮,也会进入到AddTaskForm窗口。

任务编辑窗口提供了一系列的编辑控件;

允许用户更新任务的设置,并通过使用Entity Framework中的实体类与数据库交互,将更改或添加结束保存到数据库。

C#入门学习-----任务管理系统的设计与实现(Windows Forms和LING To Entity实现)_第15张图片

在设计视图中,使用了一系列的文本框和ComboBox控件允许用户输入或选择,

AddTaskForm窗口的构造函数接收到一个TaskID参数。如果传入的TaskID的值是0, 表示用户是要新建一个任务

否则将根据传入的参数查找Task对象实例来初始化窗口。窗口构造函数如下:

 /// <summary>
        /// 重载构造函数,接收一个任务ID号
        /// </summary>
        public AddTaskForm(int taskID)
        {
            //初始化组件面板
            InitializeComponent();
            //得到传入的任务ID
            _taskID = taskID;
            //如果任务ID为0,表示为新增操作,不允许进行删除
            btnDelete.Visible = (taskID > 0);
            //初始化窗口上面的ComboBox控件,填充内容
            InitForm();
            //如果任务编号大于0
            if (taskID > 0)
            {  //调用LoadTask方法从Task中获取用户界面数据
                LoadTask();
            }
            else
            {
            }
        }


代码中,根据传入的TaskID值判断是否是0, 不管是新增还是编辑,总是会使用InitForm()方法来初始化窗口,为窗口的ComboBox控件和CheckListBox控件填充内容。

 /// <summary>
        /// method to initialize form
        /// </summary>
        private void InitForm()
        {
            BindCategory();
            BindResource();
            Utility.BindTaskDurationTypeCombo(cmbDurationType, true);
            Utility.BindTaskPriorityCombo(cmbPriority, true);
            Utility.BindTaskStatusCombo(cmbStatus, true);
            Utility.BindTaskColorCategoryCombo(cmbColorCategory, true);
            Utility.BindTaskFlagCombo(cmbTaskFlag, true);

        }

如果TaskID>0,表示要对一个已经存在的Task编辑。因此调用了LoadTask()方法加载任务。如下:

 public void LoadTask()
        {
            //使用GetTask方法从数据库中提取指定ID的Task
            Task task = GetTask(TaskID);
            if (task != null)
            {   //设置描述信息
                txtDescription.PlainText = task.Description;
                //设置过期日期
                if (task.DueDate.HasValue)
                {
                    dtpDueDate.Checked = true;
                    dtpDueDate.Value = task.DueDate.Value;
                }
                else
                {
                    dtpDueDate.Checked = false;
                    dtpDueDate.Text = string.Empty;
                }
                //从Task的属性中设置控件内容
                nudDuration.Value = task.Duration;
                cmbDurationType.Text = task.DurationType;
                dtpEndDate.Value = task.EndDate;
                nudPercentComplete.Value = task.PercentComplete;
                cmbPriority.Text = task.Priority;
                cmbTaskFlag.Text = task.Flag;
                cmbColorCategory.Text = task.ColorCategory;
                //设置任务分类
                if (task.Category != null)
                {
                    cmbCategory.Text = task.Category.Name;
                }
                dtpStartDate.Value = task.StartDate;
                cmbStatus.Text = task.Status;
                txtTitle.Text = task.Title;
                if (!task.Resource.IsLoaded)
                    task.Resource.Load();                
                foreach (Resource resource in task.Resource)
                {   //选中CheckBoxList中己经具有的资源项
                    CheckGridResource(resource);
                }
            }
        }


可以看出,代码中调用了GetTask()方法根据指定的任务ID获取Task对象,然后根据Task属性更新用户界面控件属性。

对于与Task关联的Resource和Category,通过Task的导航属性进行操作,利用Entity Framework这种特性可以大大简化传统ADO.NET的实现代码。

CheckGridResource()方法将对CheckListBox中匹配的项设置选中状态,该方法在内部调用了CheckListBox 的SetItemCheecked()方法,来设置

指定索引位置处的Checkstate状态。


 4 、9  使用期LINQ to Entity查询任务

 GetTask方法使用LINQ语句从数据库中查询指定的TaskID的Task.

其实现如下:

 /// <summary>
        /// 根据指定的TaskID返回Task对象
        /// </summary>
        private Task GetTask(int taskID)
        {   //使用LINQ语法查询指定TaskID的Task
            var matchs = from res in _taskMgrEntity.Task
                         where res.TaskID == taskID
                         select res;
            //调用IQueryable<T>的First方法返回首个对象
            Task task = matchs.First();
            //返回Task对象实例
            return task;
        }


LINQ查询的结果返回IQueryable<Task>类型的对象。通过调用IQueryable<T>的First扩展方法,返回首个

匹配的Task对象,以便用户进行编辑。

当用户编辑完成后,可以单击“保存”按钮。在实现保存前,

首先需要对用户在表单中的输入进行验证,使用ValidateForm()方法完成这一过程。如果成功,将调用SaveTask方法保存任务,同时关闭任务窗口。

保存按钮代码如下:

 private void btnSave_Click(object sender, EventArgs e)
        {
            if (!ValidateForm())//验证表单输入是否完整
            {
                Utility.ShowErrorMessage("请提供有效信息.", "保存任务错误");
                return;//返回过程代码
            }
            SaveTask();//调用SaveTask方法将任务保存回数据库
            //设置窗口的DialogResult值
            this.DialogResult = DialogResult.OK;
            this.Close();//关闭窗口
        }


ValidateForm将验证用户是否输入了任务标题,如果标题内容为空,将使用ErrorProvider控件在文本框右侧显示一个错误图标。

ErrorProvider比使用消息框(MessageBox)显示错误信息的效果好,因为一旦消息框关闭了错误信息也随之消失,用户可能无法纠正所有错误而要多次弹出错误消息框。

而ErrorProvider会记录所有的错误并能够准确定位在错误发生的窗体或控件上,

显示一个红色图标,当鼠标停在该图标上时能够自动弹出ToolTip提示错误内容。

 

4、10 保存任务到数据库

SaveTask()方法将用户在界面中的输入保存到数据库。当然代码需要判断用户是编辑一条记录还是

新增一条记录,如果是新增记录,将创建一个新的Task对象,在保存时,同时也要保存用户在资源

CheckListBox中选择的资源到TaskResource数据表中,SaveTask代码如下:

/// <summary>
        /// 保存或更新任务
        /// </summary>
        private void SaveTask()
        {
            Task task = null;//保存Task的变量
            bool isNew = false;//默认的是否新增标志
            if (TaskID > 0)//如果是编辑一条Task
            {   //从数据库中获取原始Task
                task = GetTask(TaskID);
                //同时获取与资源相关联的Resource集合
                if (!task.Resource.IsLoaded)
                    task.Resource.Load();
            }
            if (task == null)//如果新增一条Task
            {   //实例化一个新的Task对象
                task = new Database.Task();
                isNew = true;//指定新增标志为True
            }
            //从用户界面的控件输入设置Task对象的属性
            task.Description = txtDescription.PlainText;
            if (dtpDueDate.Checked)//获取过期日期的值
            {
                task.DueDate = GetDateTime(dtpDueDate.Value);
            }
            else
            {
                task.DueDate = null;
            }            
            task.Duration = (int)nudDuration.Value;//期间长度值
            task.DurationType = cmbDurationType.Text;//任务期间类型
            task.EndDate = GetDateTime(dtpEndDate.Value);//任务结束日期
            task.PercentComplete = (int)nudPercentComplete.Value;//完成百分比
            task.Priority = cmbPriority.Text;//优先级
            task.ColorCategory = cmbColorCategory.Text;//颜色分类
            task.Flag = cmbTaskFlag.Text;//标志
            Category category = null;//保存Category对象的变量
            if (cmbCategory.SelectedIndex > 0)
            {   //查询指定CategoryID的Category对象
                category = _taskMgrEntity.Category.Where("it.CategoryID=" + 
                           cmbCategory.SelectedValue).First();
            }
            task.Category = category;//为Task的Category属性赋category对象实例
            task.StartDate = GetDateTime(dtpStartDate.Value);//设置开始时间
            task.Status = cmbStatus.Text;//设置任务状态文本
            task.Title = txtTitle.Text;//获取任务标题
            //将CheckListBox中的选择添加到Task的Resource集合
            SaveResource(task);
            if (isNew)//对于新增任务,调用AddToTask方法保存
            {
                _taskMgrEntity.AddToTask(task);
            }
            _taskMgrEntity.SaveChanges();//将更改保存回数据库
        }

在保存任务时,要记得区分是新增的Task还是对已有的Task进行编辑,因此代码中命名用isNew变量来记录新增值。

很明显如果TaskID的值不是0的话,那么记录就编辑任务。

SaveResource()方法将用户在CheckListBox中选择的资源添加到Task.Resource列表中。

 4、11 删除选定的任务

 private void btnDelete_Click(object sender, EventArgs e)
        {
            DialogResult dlgResult = //显示确认删除消息
                Utility.ShowConfirmationBox("你确定要删除当前任务?", "确认");
            if (dlgResult == DialogResult.Yes)//如果用户确定删除
            {
                Task task = GetTask(TaskID);//获取要删除的Task对象
                if (task != null)//如果Task不为null
                {   //使用ObjectContext的DeleteObject从实例中删除Task
                    _taskMgrEntity.DeleteObject(task);
                    _taskMgrEntity.SaveChanges();//向数据库保存更改
                    //设置DialogReslut
                    this.DialogResult = DialogResult.OK;
                    this.Close();//关闭窗口
                }
            }
        }


 

你可能感兴趣的:(windows,数据库,C#,任务,LINQ,Forms)