C#窗体应用实战项目——绩效考核管理系统

笔者入门C#,熟悉C#语法之后,来做一个Winform项目巩固知识,记录一下学习过程。

一、什么是Winform

WinForm 是 Windows Form 的简称,是基于 .NET Framework 平台的客户端(PC软件)开发技术,一般使用C#编程。C# WinForm 编程需要创建「Windows窗体应用程序」项目。
.NET 提供了大量 Windows 风格的控件和事件,可以直接拿来使用,上手简单,开发快速
Windows 窗体应用程序是 C# 语言中的一个重要应用,也是 C# 语言最常见的应用。

二、项目背景

2.1项目介绍

绩效考核管理系统,主要根据企业员工的身份,绩效考核,来综合计算员工的薪资。

2.2涉及知识点

 窗体容器、数据库操作(Sql Server)、数据绑定与获取、委托事件、Sql参数化、泛型反射、反射、分层架构、工厂模式、缓存、泛型缓存、单例模式、动态创建控件。

2.3数据库结构

 本项目主要设计四张表,人员表Users、员工绩效表UserAppraisals、身份基数表AppraisalBases、考核系数表AppraisalCoefficients。表关系如下:

 C#窗体应用实战项目——绩效考核管理系统_第1张图片

C#窗体应用实战项目——绩效考核管理系统_第2张图片

2.3.1人员表Users

 存储员工的基本信息,如姓名、性别、密码、身份Id、软删除标记

 C#窗体应用实战项目——绩效考核管理系统_第3张图片

 创建表格SQL语句如下:


GO
/****** Object:  Table [dbo].[Users]    Script Date: 2019/12/19 21:51:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Users](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[UserName] [varchar](50) NULL,
	[Sex] [varchar](8) NULL,
	[Password] [varchar](50) NULL,
	[BaseTypeId] [int] NULL,
	[IsDel] [bit] NULL,
 CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

插入数据SQL语句如下: 


INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('张三','男','111','1','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('李四','女','111','2','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('王五','男','111','3','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('赵六1','女','111','4','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('田七','男','111','5','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('周八','女','111','6','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('吴九','女','111','6','True')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('test','男','123456','1','True')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('test2','男','111','1','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('test3','男','111','1','False')

INSERT INTO Users(UserName,Sex,Password,BaseTypeId,IsDel) VALUES('test4','男','111','1','False')

2.3.2员工绩效表UserAppraisals

用于记录所有员工的绩效情况,如请假次数,加班次数等

 C#窗体应用实战项目——绩效考核管理系统_第4张图片


GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[UserAppraisals]    Script Date: 2019/12/19 21:51:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UserAppraisals](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[UserId] [int] NULL,
	[CoefficientId] [int] NULL,
	[Count] [float] NULL,
	[AssessmentYear] [int] NULL,
	[IsDel] [bit] NULL,
 CONSTRAINT [PK_UserAppraisal] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','1','12','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','2','2','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','3','12','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','4','158','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','5','36','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('1','6','3','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','1','3','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','2','3','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','3','6','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','4','0','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','5','116','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('3','6','0','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','1','12','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','2','12','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','3','2','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','4','2','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','5','2','2018','False')

INSERT INTO UserAppraisals(UserId,CoefficientId,Count,AssessmentYear,IsDel) VALUES('5','6','12','2018','False')

2.3.3身份基数表AppraisalBases

用于记录员工对应身份的基本工资,如政法编制基本工资为20000元;行政编制基本工资为1800元

C#窗体应用实战项目——绩效考核管理系统_第5张图片

GO
/****** Object:  Table [dbo].[AppraisalBases]    Script Date: 2019/12/19 21:51:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[AppraisalBases](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[BaseType] [varchar](50) NULL,
	[AppraisalBase] [int] NULL,
	[IsDel] [bit] NULL,
 CONSTRAINT [PK_AppraisalBases_1] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('政法编制1','20000','False')

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('行政编制','18000','False')

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('事业编制(管理类)','18000','False')

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('事业编制(专业技术类)','19800','False')

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('事业编制(工勤类)','16000','False')

INSERT INTO AppraisalBases(BaseType,AppraisalBase,IsDel) VALUES('社会化用工','8000','False')

2.3.4考核系数表AppraisalCoefficients

存储考核方式对绩效的影响关系,如影响系数,计算方式(-代表扣除工资;+代表奖励工资)

C#窗体应用实战项目——绩效考核管理系统_第6张图片


GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[AppraisalCoefficients]    Script Date: 2019/12/19 21:51:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[AppraisalCoefficients](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[AppraisalType] [varchar](50) NULL,
	[AppraisalCoefficient] [float] NULL,
	[CalculationMethod] [int] NULL,
	[IsDel] [bit] NULL,
 CONSTRAINT [PK_AppraisalBases] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('请假','0.1','-1','False')

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('迟到','0.05','-1','False')

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('加班','0.1','1','False')

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('办案数量','0.0005','1','False')

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('维护次数','0.002','1','False')

INSERT INTO AppraisalCoefficients(AppraisalType,AppraisalCoefficient,CalculationMethod,IsDel) VALUES('项目开发','0.3','1','False')

 三、编码

3.1项目创建

右上角 文件-新建-项目,选择C# Windows 桌面选项下的Windows窗体应用(.NET Framework)

C#窗体应用实战项目——绩效考核管理系统_第7张图片

 C#窗体应用实战项目——绩效考核管理系统_第8张图片

3.2数据库连接

首先来了解一下C#中using关键词的三个用法:

1.引用命名空间

   类似java的import。可以导入其他文件中的类,而不必指明其详细的命名空间。

   如using System之后,就可以直接用string代替System.string

2.using别名

   给某个详细的命名空间中的某个类型取别名,用于区分不同命名空间中名字相同的类。

   如using test1=A.test;

       using test2=B.test;

3.用来简化资源的释放

   using定义了一个范围,一旦超出了这个范围,自动调用IDisposable释放调该对象资源,

   只有实现了IDisposable的类可以使用这种语法。

   using(SqlConnection conn=new SqlConnection(ConStr))

   using实质是一个try-finally语句,并在finally中调用对象的Dispose方法释放资源,以防程序在出错时,资源得不到释放。

连接数据库操作:

1.编写连接字符串,为了简洁,将连接字符串写在App.config里面

  
    
  

2.创建SqlConnection对象

namespace Appraisal_System.Utility
{
    public class SqlHelper
    {
        public static string ConStr { get; set; }

        public static DataTable ExecuteTable(string cmdText, params SqlParameter[] sqlParameters)
        {
            using(SqlConnection conn=new SqlConnection(ConStr))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(cmdText, conn);
                cmd.Parameters.AddRange(sqlParameters);
                SqlDataAdapter sda = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                sda.Fill(ds);
                return ds.Tables[0];
            }
        }

        public static int ExecuteNonQuery(string cmdText,params SqlParameter[] sqlParameters)
        {
            using (SqlConnection conn = new SqlConnection(ConStr))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(cmdText, conn);
                cmd.Parameters.AddRange(sqlParameters);
                int rows = cmd.ExecuteNonQuery();
                if (rows <= 0)
                {
                    //throw new Exception("数据库操作失败");
                }
                return rows;
            }
        }
    }
}

 params修饰可变数组,表示传入的参数个数不定;

 SqlParameter类用来实现带参数的Sql语句,防止Sql注入。该类表示SqlCommand的参数,实际调用时可以通过SqlCommand的Parameters.Add()或AddRange()方法传入Sql参数。

3.3用户管理窗体

C#窗体应用实战项目——绩效考核管理系统_第9张图片

窗体元素如下:

C#窗体应用实战项目——绩效考核管理系统_第10张图片

先介绍几个常见的窗体元素:

label:是一个标签,用来进行文字说明的

textBox:是一个输入框,输入文字。类似于html中的input标签中的text

comboBox:下拉框,常用于在多个项之间进行选择,如选择城市

checkBox:单选框

dataGridView:数据表格,以表格的形式展现数据

button:按钮

在dataGridView中添加列

C#窗体应用实战项目——绩效考核管理系统_第11张图片

编写AppraisalBases充血模型代码

充血模型和贫血模型区别:

充血模型:

  • 数据和对应的业务逻辑被封装到同一个类中。因此,这种充血模型满足面向对象的封装特性,是典型的面向对象编程风格。
  • 业务逻辑集中在 Service 类中。基于充血模型,Service 层包含 Service 类和 Domain 类两部分。Domain 是基于充血模型开发的,既包含数据,也包含业务逻辑。而 Service 类变得非常单薄。
    充血模型中,绝大多业务逻辑都应该被放在domain里面,包括持久化逻辑,而Service层是很薄的一层,仅仅封装事务和少量逻辑,不和DAO层打交道。service :组合服务也叫事务服务;model:除包含get set方法,还包含原子服务和数据持久化的逻辑

贫血模型:

  • 贫血模型是一种领域模型,其中领域对象包含很少或没有业务逻辑。是一种面向过程的编程模式,它与面向对象设计的基本思想相悖,将数据和过程结合在一起。
  • 因为贫血模型没有逻辑实现,所以逻辑基本上会放到调用贫血模型的service中,这些service类会转换领域对象的状态。
  • 贫血模型中,domain包含了不依赖于持久化的原子领域逻辑,而组合逻辑在Service层。service :组合服务,也叫事务服务。model:除包含get set方法,还包含原子服务(如获得关联model的id)。dao:数据持久化。
     

AppraisalBases实体类如下

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Appraisal_System.Utility;
namespace Appraisal_System.Models
{
    public class AppraisalBases
    {
        public int Id { get; set; }
        public string BaseType { get; set; }
        public int AppraisalBase { get; set; }
        public bool IsDel { get; set; }
        
        public static List ListAll()
        {
            List list = new List();
            DataTable dt = SqlHelper.ExecuteTable("SELECT * FROM AppraisalBases");
            foreach (DataRow dr in dt.Rows)
            {
                list.Add(dr.DataRowToModel());
            }
            return list;
        }

        private static AppraisalBases ToModel(DataRow dr)
        {
            AppraisalBases appraisalBase= new AppraisalBases();
            appraisalBase.Id = (int)dr["Id"];
            appraisalBase.BaseType = dr["BaseType"].ToString();
            appraisalBase.AppraisalBase = (int)dr["AppraisalBase"];
            appraisalBase.IsDel = (bool)dr["IsDel"];
            return appraisalBase;
        }

        public static int Update(AppraisalBases appraisal)
        {
            string sql = "UPDATE AppraisalBases SET BaseType=@BaseType,AppraisalBase=@AppraisalBase,IsDel=@IsDel WHERE Id=@Id";
            int rows = SqlHelper.ExecuteNonQuery(sql,
                new SqlParameter("@Id", appraisal.Id),
                new SqlParameter("@BaseType", appraisal.BaseType),
                new SqlParameter("@AppraisalBase", appraisal.AppraisalBase),
                new SqlParameter("@IsDel", appraisal.IsDel)
                );
            return rows;
        }
    }
}

Users实体类如下 

using Appraisal_System.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Appraisal_System.Models
{
    public class Users
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public string PassWord { get; set; }
        public string Sex { get; set; }
        public int BaseTypeId { get; set; }
        public bool IsDel { get; set; }


        public static List ListAll()
        {
            List list = new List();
            string sql= "SELECT u.Id,u.UserName,u.PassWord,u.Sex,u.BaseTypeId,u.IsDel FROM Users u";
            DataTable dt = SqlHelper.ExecuteTable(sql);
            foreach (DataRow dr in dt.Rows)
            {
                list.Add(dr.DataRowToModel());

            }
            return list;
        }
        public static int Insert(Users user)
        {
            string sql = $"INSERT INTO Users(UserName,PassWord,Sex,BaseTypeId,IsDel) VALUES(@UserName,@PassWord,@Sex,@BaseTypeId,@IsDel)";
            //using SqlParameter to set query params
            return SqlHelper.ExecuteNonQuery(sql,
                new SqlParameter("@UserName", user.UserName),
                new SqlParameter("@PassWord", user.PassWord),
                new SqlParameter("@Sex", user.Sex),
                new SqlParameter("@BaseTypeId", user.BaseTypeId),
                new SqlParameter("@IsDel", user.IsDel)
                );
        }

        public static int Update(Users user)
        {
            string sql = $"UPDATE Users SET UserName=@UserName,PassWord=@PassWord,Sex=@Sex,BaseTypeId=@BaseTypeId,IsDel=@IsDel WHERE Id=@Id";
            //using SqlParameter to set query params
            return SqlHelper.ExecuteNonQuery(sql,
                new SqlParameter("@UserName", user.UserName),
                new SqlParameter("@PassWord", user.PassWord),
                new SqlParameter("@Sex", user.Sex),
                new SqlParameter("@BaseTypeId", user.BaseTypeId),
                new SqlParameter("@IsDel", user.IsDel),
                new SqlParameter("@Id", user.Id)
                );
        }
    }

}

整个窗体代码 

using Appraisal_System.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System
{
    public delegate void DelBindDgv();//声明一个类似string/int的关键字DelBindDgv delBindDgv
    public partial class FrmUserManager : Form
    {
        DelBindDgv delBindDgv;
        public FrmUserManager()
        {
            InitializeComponent();
        }

        private void FrmUserManager_Load(object sender, EventArgs e)
        {
            BindCbx();
            BindDgv();
            delBindDgv = BindDgv;
        }

        private void BindDgv()
        {
            string userName = txtUserName.Text.Trim();
            int baseTypeId = (int)cbxBase.SelectedValue;
            bool isStop = chkIsStop.Checked;
            List userAppraisalBases = UserAppraisalBases.GetListJoinAppraisal();
            dgvUserAppraisal.AutoGenerateColumns = false;
            if (baseTypeId == 0)
            {
                dgvUserAppraisal.DataSource = userAppraisalBases.FindAll
                (m => m.UserName.Contains(userName) && m.IsDel == isStop);
            }
            else
            {
                dgvUserAppraisal.DataSource = userAppraisalBases.FindAll
                (m => m.UserName.Contains(userName) && m.BaseTypeId == baseTypeId && m.IsDel == isStop);
            }
        }

        private void BindCbx()
        {
            List appraisalBases = new List();
            //add an initial choice for "querying all"
            appraisalBases.Add(new AppraisalBases
            {
                Id = 0,
                BaseType = "-查询所有-",
                AppraisalBase = 0,
                IsDel = false
            });

            appraisalBases.AddRange(AppraisalBases.ListAll());
            //set dataSource = appraisalBases which queried 
            cbxBase.DataSource = appraisalBases;
            //DisplayMember means what we see on the framework
            cbxBase.DisplayMember = "BaseType";
            //ValueMember means its value such as Id;
            cbxBase.ValueMember = "id";

            /*cbxBase.Text = "-查询所有-";
            cbxBase.Items.Add("-查询所有-");
            foreach (var appraisalBase in appraisalBases)
            {
                cbxBase.Items.Add(appraisalBase.BaseType);
            }*/
        }



        private void button1_Click(object sender, EventArgs e)
        {
            BindDgv();
        }

        private void dgvUserAppraisal_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Right)
            {
                tsmAdd.Visible = true;
                tsmEdit.Visible = false;
                tsmStart.Visible = false;
                tsmStop.Visible = false;
            }
        }

        private void dgvUserAppraisal_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                if (e.RowIndex > -1)
                {
                    dgvUserAppraisal.Rows[e.RowIndex].Selected = true;
                    tsmAdd.Visible = true;
                    tsmEdit.Visible = true;

                    bool IsDel = (bool)dgvUserAppraisal.SelectedRows[0].Cells["IsDel"].Value;
                    if (IsDel)
                    {
                        tsmStart.Visible = true;
                    }
                    else
                    {
                        tsmStop.Visible = true;
                    }

                }
            }
        }

        private void tsmAdd_Click(object sender, EventArgs e)
        {
            FrmSetUser frmSetUser=new FrmSetUser(delBindDgv);
            frmSetUser.ShowDialog();
            //Reload bindDgv data to refresh the latest data which was inserted just now
            BindDgv();
        }

        private void tsmEdit_Click(object sender, EventArgs e)
        {
            int userId = (int)dgvUserAppraisal.SelectedRows[0].Cells["Id"].Value;
            FrmSetUser frmSetUser = new FrmSetUser(delBindDgv,userId);
            frmSetUser.ShowDialog();
        }
    }
}

 编写用户管理窗体右键事件,出现新建、编辑、启用、停用子菜单

子菜单元素为ContextMeauStrip

 C#窗体应用实战项目——绩效考核管理系统_第12张图片

新建、编辑窗体 

C#窗体应用实战项目——绩效考核管理系统_第13张图片

using Appraisal_System.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System
{
    public partial class FrmSetUser : Form
    {
        private DelBindDgv _delBindDgv;
        public FrmSetUser(DelBindDgv delBindDgv)
        {
            InitializeComponent();
            _delBindDgv = delBindDgv;
        }
        private Users _user;
        public FrmSetUser(DelBindDgv delBindDgv,int userId):this(delBindDgv)
        {
            //:this(args) like java this(args) but in C# should be after method and java in method
            _user = Users.ListAll().Find(m => m.Id == userId);

        }
        private void FrmSetUser_Load(object sender, EventArgs e)
        {
            List appraisalBases = new List();
            //add an initial choice for "querying all"
            appraisalBases.AddRange(AppraisalBases.ListAll());
            //set dataSource = appraisalBases which queried 
            cbxBase.DataSource = appraisalBases;
            //displayMember means what we see on the framework
            cbxBase.DisplayMember = "BaseType";
            //displayMember means its value such as Id;
            cbxBase.ValueMember = "Id";

            //load the user which is checked now
            if (_user != null)
            {
                txtUserName.Text = _user.UserName;
                cbxBase.SelectedValue = _user.BaseTypeId;
                cbxSex.Text = _user.Sex;
                chkIsStop.Checked = _user.IsDel;
            }
        }

        private void btnmSave_Click(object sender, EventArgs e)
        {
            string userName = txtUserName.Text.Trim();
            int baseTypeId = (int)cbxBase.SelectedValue;
            string sex = cbxSex.Text;
            bool isDel = chkIsStop.Checked;

            if (_user == null)
            {
                Users user = new Users
                {
                    UserName = userName,
                    BaseTypeId = baseTypeId,
                    IsDel = isDel,
                    Sex = sex,
                    PassWord = "111",
                };
                Users.Insert(user);
                MessageBox.Show("用户添加成功!");
            }
            else
            {
                _user.UserName = userName;
                _user.BaseTypeId = baseTypeId;
                _user.IsDel = isDel;
                _user.Sex = sex;
                Users.Update(_user);
                MessageBox.Show("用户修改成功!");
            }
            _delBindDgv(); 
            this.Close();
        }
    }
}

FrmSetUser中用到了委托机制。委托简而言之,就是将某个类的某个方法委托给其他类执行,需要Delegate关键字。

委托四部曲

1、声明委托类型

2、有一个方法包含了执行的代码

3、创建委托实例

4、调用委托实例

C#窗体应用实战项目——绩效考核管理系统_第14张图片

父类将委托对象作为构造方法中的参数传递给子类

C#窗体应用实战项目——绩效考核管理系统_第15张图片

 子类在创建的时候调用自己的构造方法,绑定委托对象。子类就可以在自己内部,调用该方法。C#窗体应用实战项目——绩效考核管理系统_第16张图片

因为子窗体是用来新建和编辑用户的,那么子窗体在提交的时候,需要进行表单的数据刷新,而表单的数据刷新是由父窗体中的BindDgv方法完成的。子窗体通过调用委托,实现了间接调用了父窗体中BindDgv的方法。

3.4基数管理窗体

C#窗体应用实战项目——绩效考核管理系统_第17张图片

工厂模式+反射+泛型缓存优化窗体的创造

场景:左侧树形菜单点击进行窗体切换时,先前都是每切换一次,就新建一个窗体,复用性差,性能低下。

解决方案:将窗体的创造交给FormFactory工厂类去执行,且每个窗体设计成单例模式,缓存在List

中。创造窗体时,利用反射创造,即根据传入的窗体类名,创造出对应的窗体类。

Assembly关键词:

通过Assembly可以动态加载程序集,并查看程序集的内部信息,其中最常用的就是Load()这个方法。
     Assembly assembly=Assembly.Load("MyAssembly");
     利用Assembly的object CreateInstance(string) 方法可以反射创建一个对象,参数0为类名。

Type关键词:

     Type是最常用到的类,通过Type可以得到一个类的内部信息,也可以通过它反射创建一个对象。一般有三个常用的方法可得到Type对象。

Activator关键词:

     需要动态的创建一个实例模型的时候,就用Activator.CreateInstance(Type type)

     以下两种方法区别仅为:创建无参数的构造方法和创建有参数的构造函数。

1. Activator.CreateInstance (Type)

2. Activator.CreateInstance (Type, Object[])

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System.Common
{
    public class FormFactory
    {
        //single instance
        private static Form form;
        private static List forms = new List();
        private static List types;
        static FormFactory()
        {
            Assembly ass = Assembly.LoadFrom("Appraisal_System.exe");
            types = ass.GetTypes().ToList();
        }
        public static Form CreateForm(string formName)
        {
            //get by reflecting by name of c#
            //Assembly ass=Assembly.Load("Appraisal_System");
            //get by path LoadFile of absolute path or LoadFrom of current path
            //string path = AppDomain.CurrentDomain.BaseDirectory;
            //Assembly ass1 = Assembly.LoadFile(path + "Appraisal_System.exe");
            HideFormAll();
            formName = formName == null ? "FrmNone" : formName;
            Form form= forms.Find(m=>m.Name==formName);
            //avoid to create the same form too many times by checking the forms
            if (form == null)
            {
                Type type = types.Find(m => m.Name == formName);
                form =(Form)Activator.CreateInstance(type);
                forms.Add(form);
            }
            return form;
        }
        public static void HideFormAll()
        {
            foreach (Form f in forms)
            {
                f.Hide();
            }
        }
    }
}

基数管理窗体的实现代码

using Appraisal_System.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System
{
    public partial class FrmBaseManager : Form
    {
        public FrmBaseManager()
        {
            InitializeComponent();
        }

        private void FrmBaseManager_Load(object sender, EventArgs e)
        {
            dgvBase.DataSource = AppraisalBases.ListAll();
        }

        private void dgvBase_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            AppraisalBases appraisal=(AppraisalBases)dgvBase.Rows[e.RowIndex].DataBoundItem;
            AppraisalBases.Update(appraisal);
        }
    }
}

3.5系数管理窗体

3.6人员绩效窗体

人员绩效窗体是用来显示所有员工的绩效明细。包括所有考核项,以及年终奖。

C#窗体应用实战项目——绩效考核管理系统_第18张图片

C#窗体应用实战项目——绩效考核管理系统_第19张图片

 子窗体,用来编辑考核项

C#窗体应用实战项目——绩效考核管理系统_第20张图片

观察表中,可知任何一个考核项,如请假、迟到、加班都会影响实发年终奖。而每个考核项有考核名称、系数、次数、计算方式。根据需求(系数、次数、计算方式)这些是隐藏不显示在表格中的,利用表格横向拓展来填充表格。

基本表:在此表上做横向拓展。

SELECT u.Id,u.UserName,u.Sex,u.BaseTypeId,a.BaseType,a.AppraisalBase,u.IsDel FROM Users u LEFT JOIN AppraisalBases a ON u.BaseTypeId=a.Id

C#窗体应用实战项目——绩效考核管理系统_第21张图片

其次,需要知道每个员工的各项考核项情况。将UserAppraisals表和AppraisalCoefficients联表查询。

C#窗体应用实战项目——绩效考核管理系统_第22张图片

 那么只要我们在上述表中对UserId和AssessmentYear筛选,就可以知道指定用户在某一年的所有考核项情况。如对于UserId=1的员工和AssessmentYear=2018情况如下:

C#窗体应用实战项目——绩效考核管理系统_第23张图片

可以看到UserId=1的员工在2018年,请假了16次,迟到2次,加班3次等等。根据这些就可以计算得出该用户在某年的总年终奖。 

 上述两个实体类AppraisalCoefficients以及UserAppraisalCoefficients代码如下:

using Appraisal_System.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Appraisal_System.Models
{
    public  class AppraisalCoefficients
    {
        public int Id { get; set; }
        public string AppraisalType { get; set; }
        public double AppraisalCoefficient { get; set; }
        public int CalculationMethod { get; set; }
        public bool IsDel { get; set; }

        public static List ListAll()
        {
            List list=new List();
            DataTable dt = SqlHelper.ExecuteTable("SELECT * FROM AppraisalCoefficients");
            foreach (DataRow dr in dt.Rows)
            {
                list.Add(dr.DataRowToModel());
            }
            return list;
        }
    }
}

using Appraisal_System.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Appraisal_System.Models
{
    public class UserAppraisalCoefficients
    {
        public int Id { get; set; }
        public int UserId { get; set; }
        public int CoefficientId { get; set; }
        public double Count { get; set; }
        public int AssessmentYear { get; set; }
        public string AppraisalType { get; set; }
        public double AppraisalCoefficient { get; set; }
        public int CalculationMethod { get; set; }
        public bool IsDel { get; set; }

        public static List ListAll()
        {
            List list=new List();
            string sql = "SELECT ua.*,ap.AppraisalType,ap.AppraisalCoefficient,ap.CalculationMethod FROM UserAppraisals ua LEFT JOIN AppraisalCoefficients ap ON ua.CoefficientId=ap.Id";
            DataTable dt=SqlHelper.ExecuteTable(sql);
            foreach (DataRow dr in dt.Rows)
            {
                list.Add(dr.DataRowToModel());
            }
            return list;
        }
    }
}

 表横向拓展代码以及完整窗体代码如下:

using Appraisal_System.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System
{
    public partial class FrmUserAppraisal : Form
    {
        public FrmUserAppraisal()
        {
            InitializeComponent();
        }
        Action bindDgv;
        private void FrmUserAppraisal_Load(object sender, EventArgs e)
        {
            SetCol();
            BindDgvUserAppraisal();
            //委托
            bindDgv = BindDgvUserAppraisal;
        }

        private void BindDgvUserAppraisal()
        {
            //get the form which need extending
            DataTable dtUser = UserAppraisalBases.GetDtJoinAppraisal();
            //get the coefficient form to extend the main form
            List appraisalCoefficients = AppraisalCoefficients.ListAll();
            //fill the dtUser by the coefficient form
            foreach (var item in appraisalCoefficients)
            {
                //add the name of coefficient
                dtUser.Columns.Add(new DataColumn
                {
                    ColumnName = "AppraisalType" + item.Id
                });
                //add the value of coefficient
                dtUser.Columns.Add(new DataColumn
                {
                    ColumnName = "AppraisalCoefficient" + item.Id
                });
                //add the calculationMethod
                dtUser.Columns.Add(new DataColumn
                {
                    ColumnName = "CalculationMethod" + item.Id
                });
            }
            //add the assessmentYear
            dtUser.Columns.Add(new DataColumn
            {
                ColumnName = "AssessmentYear"
            });
            //add the YearBonus
            dtUser.Columns.Add(new DataColumn
            {
                ColumnName = "YearBonus"
            });
            //fill the dtUser
            List userAppraisalCoefficients = UserAppraisalCoefficients.ListAll();
            for (int i = 0; i < dtUser.Rows.Count; i++)
            {
                var uacFilter = userAppraisalCoefficients.FindAll(m => m.UserId == (int)dtUser.Rows[i]["Id"] && m.AssessmentYear == Convert.ToInt32(cbxYear.Text));
                //the array of coefficients to calculation
                double[] yearBonusArray = new double[uacFilter.Count];
                for (int j = 0; j < uacFilter.Count; j++)
                {
                    //get AppraisalType对应的dtUser的ColumnName的值
                    //get Appraisal count
                    string appraisalTypeKey = "AppraisalType" + uacFilter[j].CoefficientId;
                    double appraisalTypeCountValue = uacFilter[j].Count;
                    //get Appraisal coefficient
                    string appraisalCoefficientKey = "AppraisalCoefficient" + uacFilter[j].CoefficientId;
                    double appraisalCoefficientValue = uacFilter[j].AppraisalCoefficient;
                    //get Appraisal CalculationMethod
                    string calculationMethodKey = "CalculationMethod" + uacFilter[j].CoefficientId;
                    int calculationMethodValue = (int)uacFilter[j].CalculationMethod;
                    //bind to dtUser
                    dtUser.Rows[i][appraisalTypeKey] = appraisalTypeCountValue;
                    dtUser.Rows[i][appraisalCoefficientKey] = appraisalCoefficientValue;
                    dtUser.Rows[i][calculationMethodKey] = calculationMethodValue;

                    //calculation the coefficient
                    //count*coefficient*method
                    yearBonusArray[j] = appraisalCoefficientValue * calculationMethodValue * appraisalTypeCountValue;
                }
                dtUser.Rows[i]["AssessmentYear"] = cbxYear.Text;
                //calculation the final bonus
                double yearBonusAll = 1;
                for (int j = 0; j < yearBonusArray.Length; j++)
                {
                    yearBonusAll += yearBonusArray[j];
                }
                double yearBonusSum = yearBonusAll * Convert.ToDouble(dtUser.Rows[i]["AppraisalBase"]);
                if (yearBonusSum < 0)
                {
                    yearBonusSum = 0;
                }
                dtUser.Rows[i]["YearBonus"] = yearBonusSum;
            }
            dgvUserAppraisal.AutoGenerateColumns = false;
            dgvUserAppraisal.DataSource = dtUser;
        }

        private void SetCol()
        {
            List appraisalCoefficients = AppraisalCoefficients.ListAll();
            List dataGridViewTextBoxColumns = new List();
            foreach (var coe in appraisalCoefficients)
            {
                dataGridViewTextBoxColumns.Add(new DataGridViewTextBoxColumn
                {
                    HeaderText = coe.AppraisalType,
                    Name = "AppraisalType" + coe.Id.ToString(),
                    DataPropertyName = "AppraisalType" + coe.Id.ToString(),
                    ReadOnly = true,
                    Width = 88,
                });
                dataGridViewTextBoxColumns.Add(new DataGridViewTextBoxColumn
                {
                    HeaderText = "系数",
                    Name = "AppraisalCoefficient" + coe.Id.ToString(),
                    DataPropertyName = "AppraisalCoefficient" + coe.Id.ToString(),
                    ReadOnly = true,
                    Visible = false,
                    Width = 88,
                });
                dataGridViewTextBoxColumns.Add(new DataGridViewTextBoxColumn
                {
                    HeaderText = "计算方式",
                    Name = "CalculationMethod" + coe.Id.ToString(),
                    DataPropertyName = "CalculationMethod" + coe.Id.ToString(),
                    ReadOnly = true,
                    Visible = false,
                    Width = 88,
                });
            }
            dgvUserAppraisal.Columns.AddRange(dataGridViewTextBoxColumns.ToArray());
            dgvUserAppraisal.Columns.Add(new DataGridViewTextBoxColumn
            {
                HeaderText = "考核年度",
                Name = "AssessmentYear",
                DataPropertyName = "AssessmentYear",
                ReadOnly = true,
                Width = 166
            });
            dgvUserAppraisal.Columns.Add(new DataGridViewTextBoxColumn
            {
                HeaderText = "实发年终奖",
                Name = "YearBonus",
                DataPropertyName = "YearBonus",
                ReadOnly = true,
                Width = 166
            });
        }

        private void dgvUserAppraisal_MouseDown(object sender, MouseEventArgs e)
        {
            tsmEdit.Visible= false;
        }

        private void dgvUserAppraisal_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if(e.Button == MouseButtons.Right)
            {
                if(e.RowIndex > -1)
                {
                    dgvUserAppraisal.ClearSelection();
                    dgvUserAppraisal.Rows[e.RowIndex].Selected = true;
                    tsmEdit.Visible = true;

                }
            }
        }

        private void tsmEdit_Click(object sender, EventArgs e)
        {
            string year=cbxYear.Text;
            int userId = (int)dgvUserAppraisal.SelectedRows[0].Cells["Id"].Value;
            FrmUserAppraisalEdit frmUserAppraisalEdit = new FrmUserAppraisalEdit(userId,year,bindDgv);
            frmUserAppraisalEdit.ShowDialog();
        }
    }
}

编辑考核项子窗体:利用FlowLayoutPanel以及Panel容器动态创造

这个子窗体也用到了委托机制,用于再修改后刷新用户考核项情况。

 C#窗体应用实战项目——绩效考核管理系统_第24张图片

using Appraisal_System.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Appraisal_System
{
    public partial class FrmUserAppraisalEdit : Form
    {
        private int _userId;
        private string _year;
        private Action _bindDgv;
        public FrmUserAppraisalEdit()
        {
            InitializeComponent();
        }

        public FrmUserAppraisalEdit(int userId,string year,Action bindDgv):this()
        {
            _userId = userId;
            _year = year;
            _bindDgv = bindDgv;
        }
        private void btnSave_Click(object sender, EventArgs e)
        {
            var flCtrs = flp.Controls;
            foreach (Control flCtr in flCtrs)
            {
                if (flCtr is Panel)
                {
                    var plCtrs = flCtr.Controls;
                    foreach (var plCtr in plCtrs)
                    {
                        if (plCtr is TextBox)
                        {
                            int acId = Convert.ToInt32(((TextBox)plCtr).Name.Split('_')[1]);
                            double count = Convert.ToDouble(((TextBox)plCtr).Text);
                            UserAppraisals.Delete(_userId, _year, acId);
                            UserAppraisals userAppraisals = new UserAppraisals
                            {
                                UserId = _userId,
                                CoefficientId = acId,
                                AssessmentYear = Convert.ToInt32(_year),
                                Count = count,
                                IsDel = false,
                            };
                            UserAppraisals.Insert(userAppraisals);
                        }
                    }
                }
            }
            MessageBox.Show("修改成功!");
            _bindDgv();
            this.Close();
        }

        private void FrmUserAppraisalEdit_Load(object sender, EventArgs e)
        {
            CreateContorls();
            BindControls();
        }

        private void BindControls()
        {
            List userAppraisals = UserAppraisals.ListByUserIdAndYear(_userId, _year);
            foreach (var ua in userAppraisals)
            {
                var flCtrs = flp.Controls;
                foreach (Control flCtr in flCtrs)
                {
                    if (flCtr is Panel)
                    {
                        var plCtrs = flCtr.Controls;
                        foreach (var plCtr in plCtrs)
                        {
                            if (plCtr is TextBox)
                            {
                                int acId = Convert.ToInt32(((TextBox)plCtr).Name.Split('_')[1]);
                                if (acId == ua.CoefficientId)
                                {
                                    ((TextBox)plCtr).Text = ua.Count.ToString();
                                }
                            }
                        }
                    }
                }
            }
        }

        private void CreateContorls()
        {
            List appraisalCoefficients = AppraisalCoefficients.ListAll();
            foreach (var ac in appraisalCoefficients)
            {
                Panel panel = new Panel();

                Label label = new Label
                {
                    Text = ac.AppraisalType,
                    Width = 60,
                    Location = new Point(0, 4),
                };
                TextBox txt = new TextBox
                {
                    Location = new Point(66, 0),
                    Width = 120,
                    Height = 26,
                    Name="txtAppraisalType_"+ac.Id,
                };
                panel.Controls.Add(label);
                panel.Controls.Add(txt);
                flp.Controls.Add(panel);
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

你可能感兴趣的:(c#,c#,开发语言,.net)