Winform开发实践

来源:互联网 发布:台湾同性网络剧2017 编辑:程序博客网 时间:2024/06/06 16:53

首先声明这是一篇水文,仅仅是作为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 attributesCHANGE 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>
在这里,要理解几个重要的概念,DataSetDataTable,其实,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

0 0
原创粉丝点击