关于C#窗体间的数据传值的方法好几种,在项目中都各有应用,虽然简单,这里记录下来,分享给大家!

一、使用带参数的构造函数

主窗体

1
2
3
4
5
6
private void button_Click(object sender, System.EventArgs e)
{
// 将主窗体的控件值作为参数传递到子窗体
Form2 formChild = new Form2(this.textBoxFrm1.Text, this.checkBoxFrm1.Checked);
formChild.ShowDialog();
}

子窗体(Form2)

1
2
3
4
5
6
7
// 修改默认无参窗体构造函数,接受传递过来的值,且传递给子窗体控件。
Public Form2(string text, bool checkedValue)
{
InitializeComponent();
this.textBoxFrm2.Text = text;
this.checkBoxFrm2.Checked = checkedValue;
}

但是这种传值存在不足,在子窗体里的数据修改后不能传给主窗体,也就是说主窗体不受子窗体的影响。而在实际的开发过程中我们经常使用子窗体来修改主窗体里面的数据。

二、 在.NET中有两种类型,值类型和引用类型。

值类型是从ValueType继承而来,而ValueType又是从Object继承;对于引用类型它直接继承Object类型。这下让我们看看怎样利用引用类型来通过Form2来修改Form1里的数据。

子窗体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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传值

主窗体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 保存数据的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

子窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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类型的修改并不是修改原来的值,原来的值没有变化,而是重新生成一个新的字符串,下面是一个很好的说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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事件。

主窗体的修改按钮点击处理函数如下:

1
2
3
4
5
6
7
8
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属性,

1
2
3
4
//public ArrayList ListData1
//{
// get{return this.listData1;}
//}

而在子窗体中加上ListData2属性,

1
2
3
4
5
6
7
8
9
public ArrayList ListData2
{
set
{
this.listData2 = value;
foreach (object o in this.listData2)
this.listBoxFrm2.Items.Add(o);
}
}

也可以把属性改成方法,

1
2
3
4
5
6
public void SetListData(ArrayList listData)
{
this.listData2 = listData;
foreach (object o in this.listData2)
this.listBoxFrm2.Items.Add(o);
}

而在主窗体的修改按钮处理函数中也要相应改动:

1
formChild.ListData2 = this.listData1;

改为

1
formChild.SetListData(this.listData1);

总结,我们通过Form类的Owner属性来建立主从窗体间的桥梁,这个是不是类似于把主窗体作为子窗体的构造函数参数传入实现的功能差不多;另外又采用了属性和方法来完成数据的交互,我觉得这种实现方法很实用,特别是用在不需要实例化类或着已经有了实例的情况下传递数据。下一节我们来讲如何使用静态类来完成数据的交互。

五、使用静态类

之前使用带参数的构造函数、属性以及方法实现了数据的交互,接下来的是使用静态类来完成窗体间的数据交互。这也是经常要用到的一种数据交互方法。

下面是定义的一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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