学习来源:B站全栈ACE(讲的很好,推荐!)
1.项目简介及业务逻辑和数据库设计
知识点主要涉及:父容器详解,数据库连接查询,数据绑定与获取,委托,Sql语句参数化,泛型反射,反射,分层架构,工厂模式,普通缓存,泛型缓存,单例在winform的经典使用,动态化控件放置,datagridview不定项绑定。
首先,在Sql Server中新建表(Sql Server是真难下,我下了两天,其中安装遇到无法启动服务的问题,安装到最后显示
安装 SQL Server 数据库引擎服务实例功能 时出错 SQL Server
功能“SQL_Engine_Core_Inst”所处的状态不支持修复,因为从未成功配置该功能。只能修复成功安装的功能。若要继续,请删除指定的
SQL Server 功能。 错误代码: 0x84B40002
用了很多解决方法,最后成功的解决方法附上链接:数据库SQL Server 2008安装时提示代理服务器提供的凭据无效)
在新建表的过程中,遇到一些问题:
1.想要给各个列都加备注:列属性-表设计器-说明
2.sql server怎么设置id字段自增
3.给表设置外键
问题1:没看懂这里自增了之后是怎么从1021开始的。
2.Winform的框架及树形菜单搭建
1.拖进去一个SplitContainer,把软件分成两个Panel
2.新建一个treeview
更改字体(Font)和行间距(ItemHeight),设置其停靠在父容器(右上角小三角),把前面的虚线去掉(ShowLines = false),设置满行选择(FullRowSelect = true)
3.在Panel2新建一个人员管理页面,添加windows窗体命名为FrmUserManager.cs
4.在上方添加一个groupbox(Text = 筛选),下方新建一个datagridview
5.为了让控件跟随窗口拉伸而改变大小,设置anchor属性
上方groupbox的anchor = top,left,right
下方datagridview的anchor = top(为了让上下两部分控件之间没有间隔),bottom,left,right
6.为了让右边窗体嵌套在主窗体中
- 将主窗体FrmMain的属性IsMidContainer值设置为true
- 在FrmMain.cs中加入代码
private void FrmMain_Load(object sender, EventArgs e)
{
FrmUserManager frmUserManager = new FrmUserManager();
frmUserManager.MdiParent = this;
frmUserManager.Parent = splitContainer1.Panel2;
frmUserManager.Show();//不能用ShowDialog
}
- 为了让右边窗体显示正确,把FrmUserManager窗体属性值FormBorderStyle = None,WindowState = Maximized
7. 继续新增控件
3.SqlHelper和自动释放
1.新增两个文件夹
- /Models存放实体模型
- /Utility存放工具类
2.在/Utility中新建一个SqlHelper类
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_Systerm.Utility
{
public class SqlHelper
{
// 连接字符串
public static string ConStr { get; set; }
public static DataTable ExecuteTable(string cmdText)
{
using(SqlConnection conn = new SqlConnection(ConStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand(cmdText, conn);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
sda.Fill(ds);
return ds.Tables[0];
}
}
// 取数据
public static int ExecuteNonQuery(string cmdText)
{
using (SqlConnection conn = new SqlConnection(ConStr))
{
conn.Open();
SqlCommand cmd = new SqlCommand(cmdText, conn);
int rows = cmd.ExecuteNonQuery();
if (rows <= 0)
{
throw new Exception("数据库操作失败");
}
return rows;
}
}
}
}
没找到这部分前面课程在哪讲的,只能先用着找到了再加上。
3.修改App.config设置连接字符串
<!--没有网的情况下DataSource = 127.0.0.1 如果只写一个.会报错-->
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="ConStr" connectionString="Data Source=.;database=PerformanceAppraisalDb;uid=sa;pwd=123456" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>
4.修改Program.cs代码,获取3编写的连接字符串
SqlHelper.ConStr = ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString;
4.充血模型的实体创建
充血模型:既有属性又有方法的类称为充血模型,只有属性没有方法的类称为贫血模型。
实现功能:让基数表的内容显示在页面的下拉菜单中
1.在/Models文件夹新建一个类AppraisalBases.cs,目的是获取基数表
using Appraisal_Systerm.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Appraisal_Systerm.Models
{
public class AppraisalBases
{
// 属性与数据表AppraisalBases中列的内容一致
public int Id { get; set; }
public string BaseType { get; set; }
public int AppraisalBase { get; set; }
public bool IsDel { get; set; }
// 通过List集合存储指定的类型对象的集合
public static List<AppraisalBases> ListAll()
{
List<AppraisalBases> appraisalBases = new List<AppraisalBases>();
DataTable dt = SqlHelper.ExecuteTable("SELECT * FROM AppraisalBases");
foreach(DataRow dr in dt.Rows)
{
appraisalBases.Add(ToModel(dr));
}
return appraisalBases;
}
// 把数据库里的对象转换为实体对象
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;
}
}
}
2.修改FrmUserManager.cs代码,目的是让基数表的内容显示在 页面的下拉菜单中
调试途中遇到了一些愚蠢的bug
1.无法识别的配置节 connectionString
System.Configuration.ConfigurationErrorsException
HResult=0x80131902
Message=配置系统未能初始化
Source=System.Configuration
StackTrace:
在 System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
在 System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
在 System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
在 System.Configuration.ConfigurationManager.get_ConnectionStrings()
在 Appraisal_Systerm.Program.Main() 在 D:\study\C#学习程序\全栈ACEC#基础\Appraisal_Systerm\Appraisal_Systerm\Program.cs 中: 第 19 行
内部异常 1:
ConfigurationErrorsException: 无法识别的配置节 connectionString。 (D:\study\C#学习程序\全栈ACEC#基础\Appraisal_Systerm\Appraisal_Systerm\bin\Debug\Appraisal_Systerm.exe.Config line 3)
解决办法:App.config中connectionStrings少打了一个s,所以说还得仔细看报错,上面说line3,还真是line3。
2.conn.Open()报错
System.Data.SqlClient.SqlException:“在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: Named Pipes Provider, error: 40 - 无法打开到 SQL Server 的连接)”
解决方法:把App.config中的Data Source=127.0.0.1改成.
3.列“AppraisalBase”不属于表 Table
数据库新建表时列名写错了,改了之后重新连接数据库重新运行程序还报错发现是sql server没点保存。
(只想对我自己说一句:愚蠢的人类!)
5.数据源绑定的多种方式
private void BindCbx()
{
// 三种方法
List<AppraisalBases> appraisalBases = new List<AppraisalBases>();
/* appraisalBases.Add(new AppraisalBases
{
Id = 0,
BaseType = "-查询所有-",
AppraisalBase = 0,
IsDel = false
});//在第一行加-查询所有-
appraisalBases.AddRange(AppraisalBases.ListAll());*/
appraisalBases = AppraisalBases.ListAll();
appraisalBases.Insert(0, new AppraisalBases
{
Id = 0,
BaseType = "-查询所有-",
AppraisalBase = 0,
IsDel = false
});// 先把列表加进去,再在列表所有内容前面加上-查询所有-
// 单向数据源绑定
cbxBase.DataSource = appraisalBases;
cbxBase.DisplayMember = "BaseType";
cbxBase.ValueMember = "Id";
/*// 方法一:循环集合给下拉菜单每一项赋值
// 不用的原因:获取的内容只是BaseType的文字,实际上还要通过BaseTypeId搜索对应的User
// 在下拉菜单第一项加上"-查询所有-"
cbxBase.Text = "-查询所有-";
cbxBase.Items.Add("-查询所有-");
foreach(AppraisalBases appraisalBase in appraisalBases)
{
cbxBase.Items.Add(appraisalBase.BaseType);
}*/
}
6.表连接查询与数据的自动填充
目的:显示datagridview中数据内容,因为显示的内容是两个表的合并内容
知识点:数据库连接查询(种类:内连接,左连接,右连接,全连接)
1.编写Sql查询语句,对两个表进行左连接
这里之前不太理解为什么没有查询IsDel的值,后来觉得应该是datagridview中IsDel显示的是一个单选框,所以不用添加,还可以直接操作?
SELECT u.Id,u.UserName,u.Sex,u.BaseTypeId,a.BaseType,a.AppraisalBase FROM Users u LEFT JOIN AppraisalBases a ON u.BaseTypeId= a.Id
2.在/Models新增一个UserAppraisalBases.cs类
using Appraisal_Systerm.Utility;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Appraisal_Systerm.Models
{
public class UserAppraisalBases
{
// 属性与数据表Users,AppraisalBases中列的内容一致
public int Id { get; set; }
public string UserName { get; set; }
public string Sex { get; set; }
public int BaseTypeId { get; set; }
public string BaseType { get; set; }
public int AppraisalBase { get; set; }
public bool IsDel { get; set; }
// 获取数据表
public static List<UserAppraisalBases> GetListJoinAppraisal()
{
List<UserAppraisalBases> userAppraisalBases = new List<UserAppraisalBases>();
DataTable dt = SqlHelper.ExecuteTable("SELECT u.Id,u.UserName,u.Sex,u.BaseTypeId,u.IsDel,a.BaseType,a.AppraisalBase FROM Users u LEFT JOIN AppraisalBases a ON u.BaseTypeId= a.Id");
foreach (DataRow dr in dt.Rows)
{
userAppraisalBases.Add(ToModel(dr));
}
return userAppraisalBases;
}
private static UserAppraisalBases ToModel(DataRow dr)
{
UserAppraisalBases userAppraisalBase = new UserAppraisalBases();
userAppraisalBase.Id = (int)dr["Id"];
userAppraisalBase.UserName = dr["UserName"].ToString();
userAppraisalBase.Sex = dr["Sex"].ToString();
userAppraisalBase.BaseTypeId = (int)dr["BaseTypeId"];
userAppraisalBase.BaseType =dr["BaseType"].ToString();
userAppraisalBase.AppraisalBase = (int)dr["AppraisalBase"];
userAppraisalBase.IsDel = (bool)dr["IsDel"];
return userAppraisalBase;
}
}
}
3.修改FrmUserManager.cs代码,让左链接的表在datagridview中显示出来
导入之后,表明自动显示为英文Id,UserName…,为了自定义表头,点击设计器的datagridview右上方三角编辑列,新建之后修改列的属性DataPropertyName和Name相同,和数据库表关联起来。
dgvUserAppraisal.DataSource = UserAppraisalBases.GetListJoinAppraisal();
4.取消自动填充列
进行这一步的目的是BaseTypeId也会在datagridview中显示出来,而并不需要这一列。
dgvUserAppraisal.AutoGenerateColumns = false;