首先声明这是一篇水文,仅仅是作为C#开发小白在工作中的一点总结,已经入门的童鞋请绕道。在2008年的时候,曾经短暂供职于一家公司,用C#开发过GIS中间件。一直以来,个人的工作都偏向于数据分析(严格的来说,是定位非常不清楚,浪费了很多的时间,从这个角度来看,成功很难么?当别人浑浑噩噩无所事事的时候,你有进步的方向,又怎么会失败。)跑题了,继续回来。本文的开发背景是:某区教育局要改变当前的教育评价现状,由现在依据绝对成绩进行评价改为按照增值进行相对评价。整个教育评价信息平台已经由第三方公司完成,独缺核心增值评价算法。增值评价算法由我方进行建模开发,代码用Matlab编写,现在需要写一个简单的桌面程序来嵌入该核心算法,从数据库中获取数据,然后再将结果写回。
一、界面设计
1、在界面上将学校和学生两部分的属性都打印出来,经研究被选入模型的变量,自动被勾选;这样做的好处是便于指标体系的完善和更新。如果出现和增值结果相关性较强的变量,可以在不改变模型的情况下,进行选择。
2、开始计算按钮以及进度条。由于整个项目计算时间长,进度条可以提升用户体验,让用户掌握计算的进度。
二、用到的C#图形控件
1、GroupBox和Panel
2、Label
3、CheckedListBox
4、ProgressBar
三、使用控件时,遇到的问题
0、第一个问题,如何使控件大小自适应变化
Ans:貌似并不能通过简单的设置来实现,按下不表,研究研究附录内容再说
1、GroupBox和Panel
这部分没有详细的去看资料,在布局的时候似乎略有所悟。他们的区别是Panel用于页面布局,一般来说,Panel作为父层。当然,Panel也可以嵌入到GroupBox,但实际上也是为了对GroupBox进行布局。涉及到布局的属性,如dock什么的,自己看看控件的属性列表即可
2、Label
Q:label放置到panel里面,怎么居中?
Ans:Label控件不好用,那就用TextBox空间吧!
设置TextAlign为Center,新的问题是背景和winform不一致,和CheckedListBox一样,且一运行,自动选中了TextBox里面的内容,所以,这个也不是好用的控件,还是用回Label比较合适。
感觉自己笨笨哒,直接把label放到想要的位置就好了啊!
3、CheckedListBox
①、空间背景为白色(Window),怎样设置为透明色
Ans:CheckedListBox不能直接将背景色改为透明,but,可以将背景色设置为winform的背景色,如下:
this.chklbStudent.BackColor = mainForm.DefaultBackColor;
这样改虽然可以达成目的,但是,在winform上面会报错,问题留存以后解决。
②、CheckedListBox要点击两次才能选中
Ans:第一次为获得焦点,第二次才选中,可以改成一次选中,如下设置:
checkedListBox1.CheckOnClick = true;
③、CheckedListBox的设置
Q:默认情况下,CheckedListBox只有一列,如果属性比较多,要多列并排写,怎么办?
四、具体开发
在前面,已经基本上设置好了应用的界面,如下图:
还有几点要做:1、CheckedListBox的背景色要调为透明或者调成和Mainform颜色一样;2、在winform大小改变时,版面尚不能自适应调整;3、界面会不会太丑?这是一个问题,后面可以继续讨论。下面正式进入开发:
1、将学生和学校的属性填入CheckedListBox中,涉及到从数据库里面读出属性以及和CheckedListBox进行挂载
Q:数据库怎么设计呢?
在数据里里面,已经有2个表,一个表存储学生样本、一个表存储学校样本。
显然,需要定义一个表来存储这些属性,表设计如下properities(id, properityTitle, properityName, properityType, inUse),其中properityTitle在学生表和学校表里面定义的列名称,properityName,是properityTitle对应的中文名,properityType表征是学生级还是学校级的数据,为0表示学生级,为1表示学校级。
Q:数据怎么挂载呢?
1、设计好属性表
<span style="font-size:18px;"><span style="font-size:18px;">USE HLM;
CREATE TABLE attributes (
id INT PRIMARY KEY AUTO_INCREMENT,
attrType ENUM('1', '2'),
attrTitle VARCHAR(10),
attrName VARCHAR(20),
inUse BOOLEAN DEFAULT 0
);
ALTER TABLE attributes
CHANGE COLUMN attrTitle attrTitle VARCHAR(30);</span></span>
2、获得school和student的属性然后存入属性表,以school为例
①:获得school属性
<span style="font-size:18px;"><span style="font-size:18px;">SELECT COLUMN_NAME
FROM information_schema.columns
WHERE TABLE_NAME = 'school'
AND COLUMN_NAME <> 'id'</span></span>
得到的结果如下:
②:插入attributes表
所以可以采用的方式是将COLUMN_NAME复制到Excel,然后补全其他信息,再导入attributes表;但是,在这里希望能直接用MySQL语句来进行插入。
暂时按下不表
3、在C#读取attributes表,将属性打印到CheckedListBox上。
<span style="font-size:18px;"><span style="font-size:18px;"> private void btnConfirm_Click(object sender, EventArgs e)
{
MySqlConnection myConn = new MySqlConnection(connStr);
myConn.Open();
MySqlCommand myComm = new MySqlCommand("SELECT * FROM stusample", myConn);
List<string> stuId = new List<string>();
List<string> stuName = new List<string>();
MySqlDataAdapter adapter = new MySqlDataAdapter(myComm);
DataSet ds = new DataSet();
ds.Clear();
adapter.Fill(ds);
myConn.Close();
DataTable dt = ds.Tables[0];
String[] resultArray = new String[ds.Tables[0].Rows.Count];
int[,] stuSample = new int[dt.Rows.Count,dt.Columns.Count-2];
string[] colName = {"stuId", "stuName"};
int i = 0;
foreach (DataRow thisRows in ds.Tables[0].Rows) {
resultArray[i] = Convert.ToString(thisRows[colName[1]]);
/*
for (int j = 0; j < dt.Columns.Count; j++) {
try
{
stuSample[i, j] = (int)thisRows[j + 2];
}
catch {
Console.WriteLine("Error");
}
}
*/
i++;
}
Console.Write("OK");
/*
MySqlDataReader myReader = myComm.ExecuteReader();
try
{
while (myReader.Read())
{
stuId.Add((string)myReader["stuId"]);
stuName.Add((string)myReader["stuName"]);
}
Console.Write("OK");
}
catch (Exception)
{
btnLabel.Text = "异常勒";
}
finally
{
myReader.Close();
}
*/
}</span></span>
在这里,要理解几个重要的概念,DataSet,DataTable,其实,CheckedListBox的值是可以直接和DataTable进行绑定的,代码如下:
<span style="font-size:18px;"><span style="font-size:18px;">DataSet ds=bll.GetAllStudent();
checkedListBox1.DataSource = ds.Table[0];
checkedListBox1.ValueMember = "student_id";
checkedListBox1.DisplayMember = "student_name";</span></span>
注意:在VS中CheckedListBox像ListBox一样有DataSource属性,DisplayMember和ValueMemeber属性也都是有的,只是IntelliSense不能将其智能感知出来。
Okay,将数据从attributes读出之后,在数据绑定之前,要进行数据分离,即学生和学校的属性分别放到不同的DataTable里面,以便进行绑定。涉及到一个问题,即DataSet和DataTable的条件查询方法(有时间再去研究吧),为了不纠结,这里直接做两次查询分别绑定就好了,如下:
从上图可以看出,所有的item都没有被选中,显然,接下来是写程序让已经默认选中的item被选上。实际上,在数据绑定的时候,每一个Item就对应了数据表里面的一行元组。可以由DataRowView来获得一行元组,如下:
<span style="font-size:18px;"><span style="white-space:pre"> </span>DataRowView drv = (DataRowView)chklbStudent.Items[i];
//string str = drv["attrTitle"].ToString();
Boolean inUse = Convert.ToBoolean(drv["inUse"]);
if (inUse)
{
chklbStudent.SetItemChecked(i, true);
}</span>
这样,可以获得某个Item是否被选中的值,从而初始化控件的时候就显示出来。补充完整属性列表之后,得到如下结果:
在用Mysql读取CSV数据时,出现中文乱码,解决方法为:用记事本打开csv文件,另存为时,将编码方式改为utf-8即可。
得到完整的面板之后,就要一个函数可以获得已经选中的item的valuemenber了,以作为条件去数据库里面拿数据。
<span style="font-size:18px;"><span style="white-space:pre"> </span>/// <summary>
/// 获得CheckedListBox中被选中的item的title值
/// </summary>
/// <returns>返回的格式如下:title1, title2, title3...,注意,以,分隔开</returns>
private string GetSelectedItemTitles(string chklbType) {
string selectedStudentAttrs = string.Empty;
string selectedSchoolAttrs = string.Empty;
for (int i = 0; i < chklbStudent.Items.Count; i++) {
if (chklbStudent.GetItemChecked(i))
{
this.chklbStudent.SetSelected(i, true);
selectedStudentAttrs += (String.IsNullOrEmpty(selectedStudentAttrs) ? "" : ",") + this.chklbStudent.SelectedValue.ToString();
this.chklbStudent.SetSelected(i, false);
}
}
for (int i = 0; i < chklbSchool.Items.Count; i++)
{
if (chklbSchool.GetItemChecked(i))
{
this.chklbSchool.SetSelected(i, true);
selectedSchoolAttrs += (String.IsNullOrEmpty(selectedSchoolAttrs) ? "" : ",") + this.chklbSchool.SelectedValue.ToString();
this.chklbSchool.SetSelected(i, false);
}
}
if (chklbType == "student") {
return selectedStudentAttrs;
}
else if (chklbType == "school")
{
return selectedSchoolAttrs;
}
return null;
}</span>
4、C# 与 Mysql的连接
MyDataAdapter
解决两个问题:
1、将结果写入数据库中
public void SavaData(Dictionary<string, int> tableHead, List<string> bodyTitle, string[] body, int length, string sqlCommand) {
myConn.Open();
MySqlCommand myComm = new MySqlCommand("SELECT * FROM " + sqlCommand, myConn);
MySqlDataAdapter adapter = new MySqlDataAdapter(myComm);
DataSet ds = new DataSet();
MySqlCommandBuilder mySqlBuilder = new MySqlCommandBuilder(adapter);
ds.Clear();
adapter.Fill(ds);
DataTable dt = ds.Tables[0];
adapter.FillSchema(dt, SchemaType.Mapped);
DataRow dr;
Dictionary<string, int>.KeyCollection titles = tableHead.Keys;
for (int i = 0; i < length; i++) {
dr = dt.NewRow();
foreach (string title in titles) {
dr[title] = Convert.ToInt32(tableHead[title]);
}
dr["studentId"] = body[i];
dt.Rows.Add(dr);
}
adapter.Update(dt);
myConn.Close();
}
2、数据库主键重复问题,上述代码,重复加入数据,会出现主键重复的情况,如下:
列“id”被约束为是唯一的。值“1”已存在。
附录:
1、控件自适应大小:http://blog.csdn.net/itwit/article/details/7187393
2、C#WinForm窗体事件执行次序(较完整版):http://blog.csdn.net/neok/article/details/4616265
3、C# DataTable的詳細用法:http://blog.csdn.net/hcw_peter/article/details/3980723;https://msdn.microsoft.com/zh-cn/system.windows.forms.checkedlistbox(v=vs.85)
4、WinForm(C#)CheckedlistBox绑定数据,并获得选中的值(ValueMember)和显示文本(DisplayMember:http://blog.csdn.net/yanguan55/article/details/8777662;怪异的CheckedListBox数据绑定:http://www.cnblogs.com/JuneZhang/archive/2011/12/14/2287973.html
5、利用DataAdapter更新数据库:http://www.cnblogs.com/zxh0208/archive/2010/07/07/1772853.html
6、C#中获得Dictionary的Key值:http://bbs.csdn.net/topics/350167630
7、Datatable数据更新,并非添加:http://blog.csdn.net/hbu_dcf/article/details/5655891