我本身是做医药软件售后服务的,最开始写WinForm,目的是为了做一个自己用的售后服务登记软件,能把自己做的售后登记进去,并且能到年底查看各个客户整年的维护情况,方便报告工作。于是就网上到处找教程,结果发现,然并卵,根本看不进去。于是我换了个思路,直接开写,有问题就网上找答案。这样总算稍微有点进展,不过还是大量的东西都看不懂,说实话,我毕业快10年了,读书时候就没学过怎么编程,出社会后也没摸过,现在学起来,实在有难度,好在最近几年工作中学会了SQL SERVER。于是就从SQL SERVER上入手。毕竟在学习SQL SERVER过程中,学会了如何来查自己需要的资料。好了废话到此为止,接下来说说我的第一个WinForm软件整个开发过程。

我的第一个软件是用ILMerge打包我做的WinForm程序,把生成的所有EXE和DLL打包为一个exe程序,这对于高手来说,可能是分分钟的事,但对于我这样的新手,还是很麻烦的。我在网上搜了一个黄聪的工具,但是发现他的工具我用不了,于是模仿着做呗。

首先画个界面


界面画完后,写按钮功能就得自己来了。先把所有按钮点击函数增加上

格式大概是

private void ILMergeFilePath(object sender,EventArgs e)

{

      //这是获取ILMerge文件位置用的

}

获取文件位置的办法在网上搜到了很多,基本都是用的OpenFileDialog获取设定文件,FolderBrowserDialog 获取设定文件路径,所以我也这么用好了。

在录入代码的时候,发现了OpenFileDialog和FolderBrowserDialog 下面的各种常量值,在这些常量值下面,可以随意的引用,然后我就去依次查看各个常量值都有什么作用。

看完后,就把代码都写上去就行了。写完后,发现重复代码好多。于是就把这些重复的写为一个单独的方法,在各个点击动作里面直接调用。

private void setFileName(Control ctl)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Multiselect = true;
            fileDialog.Title = "请选择文件";
            fileDialog.Filter = "所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                ctl.Text = fileDialog.FileName;
            }
            else
            {
                ctl.Text = "";
            }
        }
        private void setFilePath(Control ctl)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "请选择文件路径";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                ctl.Text = dialog.SelectedPath;
            }
            else
            {
                ctl.Text = "";
            }
        } 对于引用文件,需要多选,我就用了个richtextbox,OpenFileDialog里面有个常量Multiselect是设置是否允许多选文件的,设置为true就行了。 对于多选的结果进行拼接。我目前用的是for循环拼接的,不知道有没其他的方法拼接,更简单些。
private void MutiSelectFileName(Control Ctl,Control PathCtl)
        {
            OpenFileDialog pfd = new OpenFileDialog();
            pfd.Multiselect = true;
            pfd.Title = "选择文件";
            pfd.Filter = "所有文件(*.*)|*.*";
            if (PathCtl.Text.Trim() != "")
            {
                pfd.InitialDirectory = PathCtl.Text.Trim();
            }
            else
            {
                pfd.InitialDirectory = "c:\\";
            }
            if (pfd.ShowDialog() == DialogResult.OK)
            {
                for (int i = 0; i < pfd.SafeFileNames.Length; i++)
                {
                    if (i != pfd.SafeFileNames.Length - 1)
                        Ctl.Text = Ctl.Text + pfd.SafeFileNames[i] + "\n";
                    else
                        Ctl.Text = Ctl.Text + pfd.SafeFileNames[i];
                }
            }
        }
这样基本所有的控件都搞定了,只差一个执行了。 执行用的是ILMerge,所以就要用到CMD命令行。命令行的执行命令也是用的拼接字符串搞定的。说真的,那个命令的拼接,费了我不少时间,本来ILMerge命令都还没用熟悉,而这个命令有那么长,哎,不熟悉啊。熟悉的话估计会很快,我用了一个写入日志的方式来查看执行命令是否有错,不知道你们会怎么操作。反正我就这么搞了。
private void actionCMD()
        {
            string cmdstr1 = this.ILMergeDir.Text.Trim();
            string cmdstr2 = "ilmerge.exe /lib:" + this.OldDir.Text.Trim() + "\\" + " /ndebug /target:winexe /targetplatform:" + this.cbVersion.Text.Trim() + " /out:" + this.NewDir.Text.Trim() + "\\" + this.NewFile.Text.Trim() + ".exe " + this.MainFile.Text.Trim() + " " + this.SelectFile.Text.Replace("\n", " ");
            System.Diagnostics.Process cmd = new System.Diagnostics.Process();//定义进程
            cmd.StartInfo.FileName = "cmd.exe";//启动CMD程序
            cmd.StartInfo.UseShellExecute = false;//不使用外壳程序
            cmd.StartInfo.CreateNoWindow = true;//不创建CMD窗口
            cmd.StartInfo.RedirectStandardInput = true;//重定向输入
            cmd.StartInfo.RedirectStandardOutput = true;//重定向输出
            cmd.Start();//启动
            cmd.StandardInput.WriteLine("\"" + cmdstr1 + "\"\\" + cmdstr2 + "&&exit");//运行命令
            FileStream fs = new FileStream("cmd.log", FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(cmd.StandardOutput.ReadToEnd());
            sw.Close();
            fs.Close();
            cmd.StandardOutput.ReadToEnd();
            cmd.WaitForExit();//等待退出
            cmd.Close();//程序关闭
        }
好了现在基本功能是能完成了,执行也ok了。然后加上检查空白的功能,就算完全OK了。至于外观什么的,那确实不知道怎么搞,毕竟我没有美工基础,整不出好看的样式。检查空白的函数用了个foreach来遍历。 private bool isNull()
        {
            foreach (Control ctlp in this.Controls)
            {
                if (ctlp.GetType().ToString() == "System.Windows.Forms.Panel")
                {
                    foreach (Control ctl in ctlp.Controls)
                    {
                        if (ctl.Text == "" || ctl.Text == null)
                        {
                            if (ctl.GetType().ToString() == "System.Windows.Forms.TextBox")
                            {
                                MessageBox.Show("不允许任何空白!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                ctl.Focus();
                                return false;
                            }
                            else if (ctl.GetType().ToString() == "System.Windows.Forms.ComboBox")
                            {
                                MessageBox.Show("不允许任何空白!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                ctl.Focus();
                                return false;
                            }
                            else if (ctl.GetType().ToString() == "System.Windows.Forms.RichTextBox")
                            {
                                MessageBox.Show("不允许任何空白!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                ctl.Focus();
                                return false;
                            }
                        }
                    }
                }
            }
            return true;
        }
呼~~总算大功告成!测试一下功能。完全OK。 最终的结果传到资源里面了,嘿嘿

总结一下吧: 1,画外观,控件可以在panel里面(in),也可以在外面(outside),也可以在上面(cross),panel是外观样式,可以是显示出来的线,也可以是立体感觉的凹陷边界。按住Ctrl键拖鼠标,可以快速复制控件。可以多选控件,一起拖动。 2,写按钮动作,C#里面每个按钮动作都是一个系统事件,不管是click,press,keydow,cell,都是不同的事件而已,事件里面就包含了各种属性信息,而一般的事件前面都会有一个object,这个object代表发生事件的主体,或者是控件,或者是其他信息。 3,如果使用了流,不管是内存流,文件流,都是IO流,所以都要预读一次System.IO,网上也看到有在后面使用的时候再来引用的,不过我觉得有点麻烦,或许别人有什么特殊意义吧,现在我这水平先不去管那些细节了,如果真有特殊意义,以后再改自己的习惯吧,总的来说要用的什么东西,都得预读一次用的那个东西的上级对象。 4,流基本上都还是要关闭的,如果不关闭,可能导致进程一直都有,系统不退出进程,一直占用资源,开10次,就占用10次资源,太没必要了。 5,如果会用到大量重复性的代码,最好是根据特性单独抽出来,写成一个独立的函数或对象。这是网上都这么说的,说是后期代码好检查维护或者移植,我目前还没什么体会,不过大家都这么搞,就随便跟跟风吧,也许以后会明白。 6,每用到一个新的东西,一定要多看看VS提示,现在VS2013的提示太强大了,实在提示不太清楚的,可以F12查看定义,C#的那些模板太多了,一定多看注释才能使用,随意的轻易使用,绝对会被坑哭。比如我现在做的第2个东西,看到别人用list来填充DataGridView,我也用,结果到了做数据重现的时候,发现list泛型太弱了,根本没法。
希望有人能一起交流,如果有谁看到我写的东西有误的,欢迎指正,我毕竟是初学者,很多描述我想肯定是非常不准确的。

更多推荐

从零开始学写WinForm(一)