前言:一篇很老的文章,保存下
关于C#窗体间的数据传值的方法好几种,在项目中都各有应用,虽然简单,这里记录下来,分享给大家!
一、使用带参数的构造函数
主窗体
private void button_Click(object sender, System.EventArgs e)
{
// 将主窗体的控件值作为参数传递到子窗体
Form2 formChild = new Form2(this.textBoxFrm1.Text,this.checkBoxFrm1.Checked);
formChild.ShowDialog();
}
子窗体(Form2)
// 修改默认无参窗体构造函数,接受传递过来的值,且传递给子窗体控件。
Public Form2(string text,bool checkedValue)
{
InitializeComponent();
this.textBoxFrm2.Text = text;
this.checkBoxFrm2.Checked = checkedValue;
}
但是这种传值存在不足,在子窗体里的数据修改后不能传给主窗体,也就是说主窗体不受子窗体的影响。而在实际的开发过程中我们经常使用子窗体来修改主窗体里面的数据。
二、 在.NET中有两种类型,值类型和引用类型。
值类型是从ValueType继承而来,而ValueType又是从Object继承;对于引用类型它直接继承Object类型。这下让我们看看怎样利用引用类型来通过Form2来修改Form1里的数据。
子窗体代码
Private TextBox textBoxFrm12;
private CheckBox checkBoxFrm12;
// 构造函数接受主窗体的控件引用,因为是引用类型,所以在内存在指向的是同一对象,因此修改这个对象的值也就修改了主窗体的值。
public Form2(TextBox heckbo,CheckBox heckbox)
{
InitializeComponent();
// 赋值
this.textBoxFrm2.Text = heckbo.Text;
this.checkBoxFrm2.Checked = heckbox.Checked;
// 定位引用
this.textBoxFrm12 = heckbo;
this.checkBoxFrm12 = heckbox;
}
// 关闭子窗体代码
private void buttonOK_Click(object sender, System.EventArgs e)
{
// 给textbox和checkbox对象赋值。这里修改了同一块内存。
this.textBoxFrm12.Text = this.textBoxFrm2.Text;
this.checkBoxFrm12.Checked = this.checkBoxFrm2.Checked;
this.Close();
}
这里也实现了传值的功能,但是总觉得不是很合理,总是让两个窗体控件传来传去,很烦,如果控件多了呢。当然我们可以将需要修改值的控件封装到自定义的结构体中,但是….我们再来看看下面一种传值方式吧。
三、C#窗体间通过ArrayList传值
主窗体代码
// 保存数据的ArrayList
private ArrayList listData;
public Form1()
{
InitializeComponent();
// 初始化ArrayList
listData = new ArrayList();
listData.Add(“.Net”);
listData.Add(“Java”);
listData.Add(“XML”);
listData.Add(“WebService”);
// 绑定数据
listBox1.DataSource = listData;
}
// 打开子窗口
private void buttonOpen_Click(object sender, EventArgs e)
{
// ArrayList 作为参数传递
Form2 frm2 = new Form2(listData);
frm2.ShowDialog();
// 在子窗口修改ArrayList 后重新绑定数据
listBox1.DataSource = null;
// listBox1.Item.Clear(); 提示”设置DataSource属性后无法修改项集合”错误。
listBox1.DataSource = listData;
}
关于”设置DataSource属性后无法修改项集合”错误,参考http://www.ideaext.com/read.php/312.htm
子窗口
ArrayList listData;
public Form2(ArrayList listData)
{
InitializeComponent(); // 注意,在修改默认构造方法的时候需要保留此项,否则子窗口的控件无法实例话,提示”未将对象引用设置到对象的实例”错误。我刚刚就犯了这错误,呵呵
this.listData = listData;
foreach (object da in listData)
{
listBox1.Items.Add(da);
}
}
// 给ArrayList 里添加值
private void buttonAdd_Click(object sender, EventArgs e)
{
if (this.textBox1.Text.Trim().Length > 0)
{
this.listData.Add(this.textBox1.Text.Trim());
this.listBox1.Items.Add(this.textBox1.Text.Trim());
}
else
MessageBox.Show(“请输入添加的内容!”);
}
// 删除ArrayList 和listbox中的值
private void buttonDel_Click(object sender, EventArgs e)
{
int index = this.listBox1.SelectedIndex;
if (index != -1)
{
this.listData.RemoveAt(index);
this.listBox1.Items.RemoveAt(index);
}
else
MessageBox.Show(“请选择删除项或者没有可删除的项!”);
}
// 关闭窗口,修改后的值就重新绑定到主窗口listbox上了
private void buttonExit_Click(object sender, EventArgs e)
{
this.Close();
}
这里有一点要提醒一下,比较两个例子,我们都传的是引用类型,一个是String,另一个是ArrayList,为什么string类型不能修改主窗体的数据呢?其实在.Net中对string类型的修改并不是修改原来的值,原来的值没有变化,而是重新生成一个新的字符串,下面是一个很好的说明。
public class PdtGrp
{
[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。
四、使用自定义属性或方法
下面我们来讲讲怎样使用自定义属性或方法来完成数据修改功能而不使用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属性来建立主从窗体间的桥梁,这个是不是类似于把主窗体作为子窗体的构造函数参数传入实现的功能差不多;另外又采用了属性和方法来完成数据的交互,我觉得这种实现方法很实用,特别是用在不需要实例化类或着已经有了实例的情况下传递数据。下一节我们来讲如何使用静态类来完成数据的交互。
五、使用静态类
之前使用带参数的构造函数、属性以及方法实现了数据的交互,接下来的是使用静态类来完成窗体间的数据交互。这也是经常要用到的一种数据交互方法。
下面是定义的一个类:
using System;
using System.Collections;
namespace ZZ
{
public class AppDatas
{
//静态数据成员
private static ArrayList listData;
//静态构造函数
static AppDatas()
{
listData = new ArrayList();
listData.Add(“DotNet”);
listData.Add(“C#”);
listData.Add(“Asp.net”);
listData.Add(“WebService”);
listData.Add(“XML”);
}
//静态属性
public static ArrayList ListData
{
get{return listData;}
}
//静态方法
public static ArrayList GetListData()
{
return listData;
}
}
}
上面包含了一个静态类成员,listData,一个静态构造函数static AppDatas(),用来初始化listData的数据。还有一个静态属性ListData和一个静态GetListData()方法,他们实现了同样的功能就是返回listData。
下面是完整的代码:
Form1.cs文件
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace ZZ
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button buttonEdit;
private System.Windows.Forms.ListBox listBoxFrm1;
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
this.listBoxFrm1.DataSource = AppDatas.ListData;
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#region Windows 窗体设计器生成的代码
private void InitializeComponent()
{
this.buttonEdit = new System.Windows.Forms.Button();
this.listBoxFrm1 = new System.Windows.Forms.ListBox();
this.SuspendLayout();
//
// buttonEdit
//
this.buttonEdit.Location = new System.Drawing.Point(128, 108);
this.buttonEdit.Name = "buttonEdit";
this.buttonEdit.TabIndex = 1;
this.buttonEdit.Text = "修改";
this.buttonEdit.Click += new System.EventHandler(this.buttonEdit_Click);
//
// listBoxFrm1
//
this.listBoxFrm1.ItemHeight = 12;
this.listBoxFrm1.Location = new System.Drawing.Point(12, 8);
this.listBoxFrm1.Name = "listBoxFrm1";
this.listBoxFrm1.Size = new System.Drawing.Size(108, 124);
this.listBoxFrm1.TabIndex = 2;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(208, 141);
this.Controls.Add(this.listBoxFrm1);
this.Controls.Add(this.buttonEdit);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private void buttonEdit_Click(object sender, System.EventArgs e)
{
Form2 formChild = new Form2();
formChild.ShowDialog();
this.listBoxFrm1.DataSource = null;
this.listBoxFrm1.DataSource = AppDatas.ListData;
}
}
}
Form2.cs文件
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace ZZ
{
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button buttonOK;
private System.ComponentModel.Container components = null;
private System.Windows.Forms.ListBox listBoxFrm2;
private System.Windows.Forms.Button buttonAdd;
private System.Windows.Forms.Button buttonDel;
private System.Windows.Forms.TextBox textBoxAdd;
public Form2()
{
InitializeComponent();
foreach(object o in AppDatas.ListData)
this.listBoxFrm2.Items.Add(o);
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void buttonOK_Click(object sender, System.EventArgs e)
{
this.Close();
}
private void buttonAdd_Click(object sender, System.EventArgs e)
{
if(this.textBoxAdd.Text.Trim().Length>0)
{
AppDatas.ListData.Add(this.textBoxAdd.Text.Trim());
this.listBoxFrm2.Items.Add(this.textBoxAdd.Text.Trim());
}
else
MessageBox.Show("请输入添加的内容!");
}
private void buttonDel_Click(object sender, System.EventArgs e)
{
int index = this.listBoxFrm2.SelectedIndex;
if(index!=-1)
{
AppDatas.ListData.RemoveAt(index);
this.listBoxFrm2.Items.RemoveAt(index);
}
else
MessageBox.Show("请选择删除项!");
}
}
}
调试可以看到实现了同样的功能。
总结,笔者认为使用静态类比较多的地方就是把应用程序的配置文件装载到一个静态类里面,让所有的窗体和其他实例都可以通过静态属性以及静态方法使用这些数据,比如三层结构或多层结构都可以访问它,而不是在多个实例间传来传去。在这里我们讨论的是Windows窗体,其实在两个不同的实例间交互数据,都可以采用文章中的方案实现,除非是这个类特有的属性或着方法。