在中轻松掌握Windows窗体间的数据交互(一) |
zhzuo(原作) 来源:csdn |
Windows 窗体是用于 Microsoft Windows 应用程序开发的、基于 .NET Framework 的新平台。此框架提供一个有条理的、面向对象的、可扩展的类集,它使您得以开发丰富的 Windows 应用程序。一个Windows窗体就代表了.NET架构里的System.Windows.Forms.Form类的一个实例。 作者在CSDN技术论坛.NET板块下的C#分类经常看到有人问起如何在两个Form间传递数据,访问修改对方窗体里面的值。对于有经验的程序员来说不是什么高深的东西,而对于初学者来说这些基础的东西往往是一个问题,并且存在这种现象,往往比较复杂的东西他们会,要用什么了就去学什么,实际上并没有真正的去理解掌握它,基础不扎实,所以就有了想通过自己对窗体编程积累的经验来写一些这方面的文章,以供学.NET的朋友参考,也借此机会同各位朋友进行交流,写得不合理的地方请各位朋友提宝贵意见,下面我分了三个部分来讲。 一.使用带参数的构造函数 我们要做的准备工作就是新建两个窗体,下面是两个窗体的布局,很简单: <第一个例子> 说明:Form1为主窗体,包含控件:文本框textBoxFrm1,多选框checkBoxFrm1和按钮buttonEdit; Form2为子窗体,包含控件:文本框textBoxFrm2,多选框checkBoxFrm2和按钮buttonOK,buttonCancel。 当我们新建一个窗体的时候,设计器会生成默认的构造函数: public Form2() { InitializeComponent(); } 它不带参数,既然我们要把Form1中的一些数据传到Form2中去,为什么不在Form2的构造函数里做文章呢? 假设我们要实现使Form2中的文本框显示Form1里textBoxFrm1的值,修改子窗体的构造函数: public Form2(string text) { InitializeComponent(); &BoxFrm2.Text = text; } 增加Form1中的修改按钮点击事件,处理函数如下: private void buttonEdit_Click(object sender, System.EventArgs e) { Form2 formChild = new BoxFrm1.Text); formChild.Show(); } 我们把BoxFrm1.Text作为参数传到子窗体构造函数,以非模式方式打开,这样打开的formChild的文本框就显示了”主窗体”文本,是不是很简单,接下来我们传一个boolean数据给子窗体。 Public Form2(string text,bool checkedValue) { InitializeComponent(); &BoxFrm2.Text = text; this.checkBoxFrm2.Checked = checkedValue; } 在主窗体中的修改按钮点击处理,我采用了打开模式窗口的方式,其实在这个例子中看不出有什么分别, private void buttonEdit_Click(object sender, System.EventArgs e) { Form2 formChild = new BoxFrm1.Text,this.checkBoxFrm1.Checked); formChild.ShowDialog(); } 结果在预料之中,但是这里明显存在不足,在子窗体里的数据修改后不能传给主窗体,也就是说主窗体不受子窗体的影响。而在实际的开发过程中我们经常使用子窗体来修改主窗体里面的数据,那怎么解决呢? 在.NET中有两种类型,值类型和引用类型。值类型是从ValueType继承而来,而ValueType又是从Object继承;对于引用类型它直接继承Object类型。这下让我们看看怎样通过Form2来修改Form1里的数据。 还是让我们来修改Form2的代码。 Private TextBox textBoxFrm12; private CheckBox checkBoxFrm12; public Form2(TextBox heckbo,CheckBox heckbox) { InitializeComponent(); &BoxFrm2.Text = heckbo.Text; this.checkBoxFrm2.Checked = heckbox.Checked; &BoxFrm12 = heckbo; this.checkBoxFrm12 = heckbox; } 现在我们传了两个引用类型的数据:TextBox类型,和CheckBox;另外在Form2中增加了两个类数据成员textBoxFrm12、checkBoxFrm12用来分别保存构造函数传来的变量,不过他们并不属于Form2的Controls容器。修改Form2的确定按钮点击事件函数: private void buttonOK_Click(object sender, System.EventArgs e) { &BoxFrm12.Text = BoxFrm2.Text; this.checkBoxFrm12.Checked = this.checkBoxFrm2.Checked; this.Close(); } 上面的代码我们通过把textBoxFrm2的Text和checkBoxFrm2.Checked赋给textBoxFrm12和checkBoxFrm12完成了对主窗体中的textBoxFrm1和checkBoxFrm2的修改,因为textBoxFrm1和textBoxFrm12是同一个引用,而checkBoxFrm2和checkBoxFrm12也是。 到这里为止功能是实现了,但是总觉得不是很合理,让两个窗体控件传来传去,现在我举一个恰当一点的例子。 修改了两个窗体: <第二个例子> 说明:在这个例子中我们的两个窗体都加了一个ListBox用来显示ArrayList中的内容。 主窗体中控件:listBoxFrm1,buttonEdit; 子窗体中控件:listBoxFrm2,textBoxAdd,buttonAdd,buttonEdit,buttonOK。 这次我们用ArrayList来作为传递数据,在Form1中定义类数据成员: private ArrayList listData1; 在构造函数中增加了对listData1进行内存分配,并生成数据最终绑定到listBoxFrm1, public Form1() { InitializeComponent(); this.listData1 = new ArrayList(); this.listData1.Add(DotNet); this.listData1.Add(C#); this.listData1.Add(Asp); this.listData1.Add(WebService); this.listData1.Add(XML); this.listBoxFrm1.DataSource = this.listData1; } 另外,对修改按钮点击事件处理函数的修改如下: private void buttonEdit_Click(object sender, System.EventArgs e) { Form2 formChild = new Form2(this.listData1); formChild.ShowDialog(); this.listBoxFrm1.DataSource = null; this.listBoxFrm1.DataSource = this.listData1; } 相对与主窗体,对子窗体作相应修改,也在Form2中增加了类数据成员: private ArrayList listData2; 用来保存对主窗体中listData1的引用。 修改构造函数: public Form2(ArrayList listData) { InitializeComponent(); this.listData2 = listData; foreach(object o in this.listData2) { this.listBoxFrm2.Items.Add(o); } } 这里让listData2同listData1指向同一个引用;另外没有对listBoxFrm进行绑定,采用了填充。 好了,下面是对数据操作的时候了。 添加处理函数代码如下: private void buttonAdd_Click(object sender, System.EventArgs e) { BoxAdd.Text.Trim().Length>0) { this.listData2.BoxAdd.Text.Trim()); this.listBoxFrm2.Items.BoxAdd.Text.Trim()); } else MessageBox.Show(请输入添加的内容!); } 删除处理代码如下: private void buttonDel_Click(object sender, System.EventArgs e) { int index = this.listBoxFrm2.SelectedIndex; if(index!=-1) { this.listData2.RemoveAt(index); this.listBoxFrm2.Items.RemoveAt(index); } else MessageBox.Show(请选择删除项或者没有可删除的项!); } 退出Form2子窗体: private void buttonOK_Click(object sender, System.EventArgs e) { this.Close(); } 编译运行程序,在子窗体中对数据进行修改,关闭后,主窗体就会显示更新后的数据。 这里有一点要提醒一下,比较两个例子,我们都传的是引用类型,一个是String,另一个是ArrayList,为什么string类型不能修改主窗体的数据呢?其实在.Net中对string类型的修改并不是修改原来的值,原来的值没有变化,而是重新生成一个新的字符串,下面是一个很好的说明。 public class ZZConsole { [STAThread] static void Main(string[] args) { string str1 = abc; string str2 = str1; str1 = 123; Console.WriteLine(str1); Console.WriteLine(--------------); Console.WriteLine(str2); Console.WriteLine(--------------); ArrayList al1 = new ArrayList(); al1.Add(abc); ArrayList al2 = al1; al2.Add(123); foreach(object o in al1) Console.WriteLine((string)o); Console.WriteLine(--------------); foreach(object o in al2) Console.WriteLine((string)o); Console.ReadLine(); } } 运行一下看看输出结果就明白了,另外对值类型的数据操作要使用ref关键字。 总结,我们通过带参数的构造函数实现了窗体间的数据交互,代码看上去也比较清楚,在实际开发过程中,可以把DataSet,DataTable,或者是DataView当作参数,当然如果只是想修改一行,可以传个DataRow或者DataRowView。在下面的文章中我们来看看怎样使用另外两种方法来实现数据的交互。 |
在中轻松掌握Windows窗体间的数据交互(二) |
zhzuo(原作) 来源:csdn |
《在中轻松掌握Windows窗体间的数据交互(一)》一文中我们讲了使用带参数的构造函数来实现窗体间的数据传递,我认为是用的比较多的一种,接下来让我们看看另外两种实现方法。o:p> 二.给窗体添加属性或方法 1.使用Form类的Owner属性 获取或设置拥有此窗体的窗体。若要使某窗体归另一个窗体所有,请为其 Owner 属性分配一个对将成为所有者的窗体的引用。当一个窗体归另一窗体所有时,它便随着所有者窗体最小化和关闭。例如,如果 Form2 归窗体 Form1 所有,则关闭或最小化 Form1 时,也会关闭或最小化Form2。并且附属窗体从不显示在其所有者窗体后面。可以将附属窗体用于查和替换窗口之类的窗口,当选定所有者窗体时,这些窗口不应消失。若要确定某父窗体拥有的窗体,请使用OwnedForms属性。 上面是SDK帮助文档上讲的,下面我们就来使用它。 首先还是使用第一篇文章中的第二个例子,窗体如下: 说明:在这个例子中我们的两个窗体都加了一个ListBox用来显示ArrayList中的内容。 主窗体中控件:listBoxFrm1,buttonEdit; 子窗体中控件:listBoxFrm2,textBoxAdd,buttonAdd,buttonEdit,buttonOK。 主窗体中还是定义类数据成员, private ArrayList listData1; 在构造函数里实例化它,填充数据,最后绑定到listBoxFrm1。 构造函数如下: public Form1() { InitializeComponent(); this.listData1 = new ArrayList(); this.listData1.Add(DotNet); this.listData1.Add(C#); this.listData1.Add(Asp); this.listData1.Add(WebService); this.listData1.Add(XML); this.listBoxFrm1.DataSource = this.listData1; } 主窗体的修改按钮处理函数: private void buttonEdit_Click(object sender, System.EventArgs e) writeline方法属于类{ Form2 formChild = new Form2(); formChild.Owner = this; formChild.ShowDialog(); this.listBoxFrm1.DataSource = null; this.listBoxFrm1.DataSource = this.listData1; } 我们设置了formChild.Owner为this,这样,子窗体和主窗体就有联系了, 当然我们也可以改成如下: private void buttonEdit_Click(object sender, System.EventArgs e) { Form2 formChild = new Form2(); formChild.ShowDialog(this); this.listBoxFrm1.DataSource = null; this.listBoxFrm1.DataSource = this.listData1; } 不过这样还不行,目前主窗体的listData1变量外部访问不到, private ArrayList listData1; 必须修改为public访问修饰符, public ArrayList listData1; 也可以通过属性(property)来实现, public ArrayList ListData1 { get{return this.listData1;} } 这里我采用属性,感觉语法更灵活,清楚。 下面是对Form2的修改, 构造函数又恢复原貌了。 public Form2() { InitializeComponent(); } 另外又新增了一个窗体的Load事件,在它的事件处理函数中来获取主窗体中的数据, private void Form2_Load(object sender, System.EventArgs e) { Form1 pareForm = (Form1)this.Owner; this.listData2 = pareForm.ListData1; foreach(object o in this.listData2) this.listBoxFrm2.Items.Add(o); } 有人会问,为什么不把上面的代码放到构造函数里面去呢?如下不是更好, public Form2() { InitializeComponent(); Form1 pareForm = (Form1)this.Owner; this.listData2 = pareForm.ListData1; foreach(object o in this.listData2) this.listBoxFrm2.Items.Add(o); } 那我会对你说错了,因为在主窗体修改按钮被点击后,开始执行 Form2 formChild = new Form2(); 而在Form2的实例化过程中会在构造函数中执行 Form1 pareForm = (Form1)this.Owner; 而这时的this.Owner是没有值的,为空引用,那么下面的代码肯定也出问题, this.listData2 = pareForm.ListData1; foreach(object o in this.listData2) this.listBoxFrm2.Items.Add(o); 当整个Form2实例化完成后,才会执行 formChild.Owner = this; 这条代码,所以使用了Form2_Load事件。 那怎样可以不使用Form2_Load事件呢?等下面我们来修改代码实现它。 下面的子窗体代码没有变化, private void buttonAdd_Click(object sender, System.EventArgs e) { BoxAdd.Text.Trim().Length>0) { this.listData2.BoxAdd.Text.Trim()); this.listBoxFrm2.Items.BoxAdd.Text.Trim()); } else MessageBox.Show(请输入添加的内容!); } private void buttonDel_Click(object sender, System.EventArgs e) { int index = this.listBoxFrm2.SelectedIndex; if(index!=-1) { this.listData2.RemoveAt(index); this.listBoxFrm2.Items.RemoveAt(index); } else MessageBox.Show(请选择删除项!); } private void buttonOK_Click(object sender, System.EventArgs e) { this.Close(); } 好了,结果同第一篇中的一样,子窗体能修改主窗体的值。 2.使用自定义属性或方法 下面我们来讲讲怎样使用自定义属性或方法来完成数据修改功能而不使用Form2_Load事件。 主窗体的修改按钮点击处理函数如下: private void buttonEdit_Click(object sender, System.EventArgs e) { Form2 formChild = new Form2(); formChild.ListData2 = this.listData1; formChild.ShowDialog(); this.listBoxFrm1.DataSource = null; this.listBoxFrm1.DataSource = this.listData1; } 并且我们去掉了主窗体的ListData1属性, //public ArrayList ListData1 //{ // get{return this.listData1;} //} 而在子窗体中加上ListData2属性, public ArrayList ListData2 { set { this.listData2 = value; foreach(object o in this.listData2) this.listBoxFrm2.Items.Add(o); } } 也可以把属性改成方法, public void SetListData(ArrayList listData) { this.listData2 = listData; foreach(object o in this.listData2) this.listBoxFrm2.Items.Add(o); } 而在主窗体的修改按钮处理函数中也要相应改动: formChild.ListData2 = this.listData1; 改为 formChild.SetListData(this.listData1); 总结,我们通过Form类的Owner属性来建立主从窗体间的桥梁,这个是不是类似于把主窗体作为子窗体的构造函数参数传入实现的功能差不多;另外又采用了属性和方法来完成数据的交互,我觉得这种实现方法很实用,特别是用在不需要实例化类或着已经有了实例的情况下传递数据。下一篇文章我们来讲如何使用静态类来完成数据的交互。 |
在中轻松掌握Windows窗体间的数据交互(三) |
zhzuo(原作) 来源:csdn |
| ||
良好的编码习惯
软件的质量属性
鲁棒 - Solid and Robust Code
简洁 - Maintainable and Simple Code
高效 - Fast Code
简短 - Small Code
共享 - Re-usable Code
可测试 - Testable Code
可移植 - Portable Code
集百家之长, 归我所用 - Follow Basic Coding Style
1.代码能够清晰的表达你的思路
2.代码应该具备自解释能力,注释代码别是单纯解释语句,这种注释毫无意义
鲁棒 - Solid and Robust Code
简洁 - Maintainable and Simple Code
高效 - Fast Code
简短 - Small Code
共享 - Re-usable Code
可测试 - Testable Code
可移植 - Portable Code
集百家之长, 归我所用 - Follow Basic Coding Style
1.代码能够清晰的表达你的思路
2.代码应该具备自解释能力,注释代码别是单纯解释语句,这种注释毫无意义
3.编码的缩进和排版规范
4.所有的函数和变量应有他人容易理解的名字
5.将Tab键改用为4个空格字符
6.减少单个函数的长度,控制在50-100行以内
7.避免幻数,多使用枚举和常量的定义
取个好名字 - Use Naming Conventions
1.采用匈牙利命名法对变量进行命名
2.名字要清晰表达含义,不要怕长
凌波微步, 未必摔跤 - Evil goto’s? Maybe Not…
1.goto的使用应该遵循原则,而不是全盘否定
2.不用写高深晦涩的语句,不要一味追求性能忽视代码可读性
4.所有的函数和变量应有他人容易理解的名字
5.将Tab键改用为4个空格字符
6.减少单个函数的长度,控制在50-100行以内
7.避免幻数,多使用枚举和常量的定义
取个好名字 - Use Naming Conventions
1.采用匈牙利命名法对变量进行命名
2.名字要清晰表达含义,不要怕长
凌波微步, 未必摔跤 - Evil goto’s? Maybe Not…
1.goto的使用应该遵循原则,而不是全盘否定
2.不用写高深晦涩的语句,不要一味追求性能忽视代码可读性
3.模式并不是一味正确,特定问题更需要考虑反模式
先发制人, 后发制于人- Practice Defensive Coding
1.尽量保持代码的简洁和简单
2.调用其它接口和函数时候首先对返回值进行检查
3.避免有符号/无符号,32位/16位,被零除等误算情况
见招拆招, 滴水不漏 - Handle The Error Cases: They Will Occur!
1.通过异常处理机制来保证程序代码的健壮性
2.异常处理中一定要注意资源的释放
3.异常处理要关注日志的详细记录,便于后续BUG分析
4.不用把后台编码或系统异常直接抛给用户
先发制人, 后发制于人- Practice Defensive Coding
1.尽量保持代码的简洁和简单
2.调用其它接口和函数时候首先对返回值进行检查
3.避免有符号/无符号,32位/16位,被零除等误算情况
见招拆招, 滴水不漏 - Handle The Error Cases: They Will Occur!
1.通过异常处理机制来保证程序代码的健壮性
2.异常处理中一定要注意资源的释放
3.异常处理要关注日志的详细记录,便于后续BUG分析
4.不用把后台编码或系统异常直接抛给用户
熟习剑法刀术, 所向无敌 - Learn Win32 API Seriously
1.Win32 API是微软平台编程根本
2.对系统强大的公用类库的熟悉和整理将事半功倍
双手互搏, 无坚不摧 - Test, but don’t stop there
1.如果你没有进行测试,你完成的代码将仅仅是个半成品
2.尽可能多的对自己的代码进行测试
3.编码人员应该更专注于白盒测试和单元测试
4.要善于使用JUnit,NUnit,PureCoverage,Compuware,NCover等测试工具
5.相互间的Review和走查是对代码可维护性的重要测试手段
6.有特殊性能要求时候需要对相关功能或模块单独进行性能测试
活用断言 - Use, don’t abuse, assertions
1.Win32 API是微软平台编程根本
2.对系统强大的公用类库的熟悉和整理将事半功倍
双手互搏, 无坚不摧 - Test, but don’t stop there
1.如果你没有进行测试,你完成的代码将仅仅是个半成品
2.尽可能多的对自己的代码进行测试
3.编码人员应该更专注于白盒测试和单元测试
4.要善于使用JUnit,NUnit,PureCoverage,Compuware,NCover等测试工具
5.相互间的Review和走查是对代码可维护性的重要测试手段
6.有特殊性能要求时候需要对相关功能或模块单独进行性能测试
活用断言 - Use, don’t abuse, assertions
1.断言可以很好的描述假设和不可能的情况
2.断言对程序Debug很有用,可以尽早的发现程序问题
草木皆兵, 不可大意 - Avoid Assumptions
1.考虑到用户使用的各种场景
2.不用假设用户会正确输入数据,要做好各种完整性和边界的检验
3.程序中70%左右代码是为了保证这种完整性服务的,正常条件下功能可能30%代码就实现了
最高境界, 无招胜有招 - Stop writing so much code
1.一味拷贝粘贴代码就是在制造拷贝BUG,这种代码对系统无任何意义
2.编码过程注意重用,函数级->组件级->系统级
3.通过重构持续改进代码质量,改进自我逻辑思维
最后总结下:
1.养成良好编码习惯,你面试的一小段代码可能就足以展现你全部陋习。
2.熟练使用好各种辅助工具,但不要全部依赖工具,最主要是学习分析和设计的思考方式
3.注重单元测试,关注程序性能,可维护性,可测试性是编码技能提升重要手段
4.通过重构使编码过程形成完整闭环的反馈回路,重构能力可以很好体现自己的设计能力
5.养成良好习惯,形成自己的编码过程检查单,多请教老员工可能事半功倍。
最后总结下:
1.养成良好编码习惯,你面试的一小段代码可能就足以展现你全部陋习。
2.熟练使用好各种辅助工具,但不要全部依赖工具,最主要是学习分析和设计的思考方式
3.注重单元测试,关注程序性能,可维护性,可测试性是编码技能提升重要手段
4.通过重构使编码过程形成完整闭环的反馈回路,重构能力可以很好体现自己的设计能力
5.养成良好习惯,形成自己的编码过程检查单,多请教老员工可能事半功倍。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论