在.net中輕鬆掌握Windows窗體間的數據交互

在.net中輕鬆掌握Windows窗體間的數據交互

By zhzuo(秋楓)  轉載自:CSDN

Windows 窗體是用於 Microsoft Windows 應用程序開發的、基於 .NET Framework 的新平臺。此框架提供一個有條理的、面向對象的、可擴展的類集,它使您得以開發豐富的 Windows 應用程序。一個Windows窗體就代表了.NET架構裏的System.Windows.Forms.Form類的一個實例。

作者在CSDN技術論壇.NET板塊下的C#分類經常看到有人問起如何在兩個Form間傳遞數據,訪問修改對方窗體裏面的值。對於有經驗的程序員來說不是什麼高深的東西,而對於初學者來說這些基礎的東西往往是一個問題,並且存在這種現象,往往比較複雜的東西他們會,要用什麼了就去學什麼,實際上並沒有真正的去理解掌握它,基礎不紮實,所以就有了想通過自己對窗體編程積累的經驗來寫一些這方面的文章,以供學.NET的朋友參考,也藉此機會同各位朋友進行交流,寫得不合理的地方請各位朋友提寶貴意見,下面我分了三個部分來講。

一.使用帶參數的構造函數
    我們要做的準備工作就是新建兩個窗體,下面是兩個窗體的佈局,很簡單:
2004112916299694.jpg20041129162916596.jpg
<第一個例子>

說明:Form1爲主窗體,包含控件:文本框textBoxFrm1,多選框checkBoxFrm1和按鈕buttonEdit;

Form2爲子窗體,包含控件:文本框textBoxFrm2,多選框checkBoxFrm2和按鈕buttonOK,buttonCancel。

當我們新建一個窗體的時候,設計器會生成默認的構造函數:

public Form2()

{

     InitializeComponent();

}

它不帶參數,既然我們要把Form1中的一些數據傳到Form2中去,爲什麼不在Form2的構造函數裏做文章呢?

假設我們要實現使Form2中的文本框顯示Form1裏textBoxFrm1的值,修改子窗體的構造函數:

public Form2(string text)

{

     InitializeComponent();

     this.textBoxFrm2.Text = text;

}

增加Form1中的修改按鈕點擊事件,處理函數如下:

private void buttonEdit_Click(object sender, System.EventArgs e)

{

     Form2 formChild = new Form2(this.textBoxFrm1.Text);

     formChild.Show();

}

我們把this.textBoxFrm1.Text作爲參數傳到子窗體構造函數,以非模式方式打開,這樣打開的formChild的文本框就顯示了”主窗體”文本,是不是很簡單,接下來我們傳一個boolean數據給子窗體。

Public Form2(string text,bool checkedValue)

{

     InitializeComponent();

     this.textBoxFrm2.Text = text;

     this.checkBoxFrm2.Checked = checkedValue;

}

在主窗體中的修改按鈕點擊處理,我採用了打開模式窗口的方式,其實在這個例子中看不出有什麼分別,

private void buttonEdit_Click(object sender, System.EventArgs e)

{

     Form2 formChild = new Form2(this.textBoxFrm1.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();

     this.textBoxFrm2.Text = heckbo.Text;

     this.checkBoxFrm2.Checked = heckbox.Checked;

     this.textBoxFrm12 = heckbo;

     this.checkBoxFrm12 = heckbox;

}

現在我們傳了兩個引用類型的數據:TextBox類型,和CheckBox;另外在Form2中增加了兩個類數據成員textBoxFrm12、checkBoxFrm12用來分別保存構造函數傳來的變量,不過他們並不屬於Form2的Controls容器。修改Form2的確定按鈕點擊事件函數:

private void buttonOK_Click(object sender, System.EventArgs e)

{

     this.textBoxFrm12.Text = this.textBoxFrm2.Text;

     this.checkBoxFrm12.Checked = this.checkBoxFrm2.Checked;

     this.Close();

}

上面的代碼我們通過把textBoxFrm2的Text和checkBoxFrm2.Checked賦給textBoxFrm12和checkBoxFrm12完成了對主窗體中的textBoxFrm1和checkBoxFrm2的修改,因爲textBoxFrm1和textBoxFrm12是同一個引用,而checkBoxFrm2和checkBoxFrm12也是。

到這裏爲止功能是實現了,但是總覺得不是很合理,讓兩個窗體控件傳來傳去,現在我舉一個恰當一點的例子。

修改了兩個窗體:
20041129162948368.jpg20041129162955233.jpg
<第二個例子>

說明:在這個例子中我們的兩個窗體都加了一個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.net");

     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)

{

     if(this.textBoxAdd.Text.Trim().Length>0)

     {

         this.listData2.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)

     {

         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。在下面的文章中我們來看看怎樣使用另外兩種方法來實現數據的交互。


二.給窗體添加屬性或方法

1.使用Form類的Owner屬性

獲取或設置擁有此窗體的窗體。若要使某窗體歸另一個窗體所有,請爲其 Owner 屬性分配一個對將成爲所有者的窗體的引用。當一個窗體歸另一窗體所有時,它便隨着所有者窗體最小化和關閉。例如,如果 Form2 歸窗體 Form1 所有,則關閉或最小化 Form1 時,也會關閉或最小化 Form2。並且附屬窗體從不顯示在其所有者窗體後面。可以將附屬窗體用於查找和替換窗口之類的窗口,當選定所有者窗體時,這些窗口不應消失。若要確定某父窗體擁有的窗體,請使用OwnedForms屬性。

上面是SDK幫助文檔上講的,下面我們就來使用它。

首先還是使用第一篇文章中的第二個例子,窗體如下:
20041129163032517.jpg20041129163039187.jpg
說明:在這個例子中我們的兩個窗體都加了一個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.net");

     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();

     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)

{

     if(this.textBoxAdd.Text.Trim().Length>0)

     {

         this.listData2.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)

     {

         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屬性來建立主從窗體間的橋樑,這個是不是類似於把主窗體作爲子窗體的構造函數參數傳入實現的功能差不多;另外又採用了屬性和方法來完成數據的交互,我覺得這種實現方法很實用,特別是用在不需要實例化類或着已經有了實例的情況下傳遞數據。下一篇文章我們來講如何使用靜態類來完成數據的交互。

三.使用靜態類

下面是定義的一個類:

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());

         }

         private void InitializeComponent()

         {

              this.buttonEdit = new System.Windows.Forms.Button();

              this.listBoxFrm1 = new System.Windows.Forms.ListBox();

              this.SuspendLayout();

              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);

              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;

              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);

         }

         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 InitializeComponent()

         {

              this.buttonOK = new System.Windows.Forms.Button();

              this.listBoxFrm2 = new System.Windows.Forms.ListBox();

              this.buttonAdd = new System.Windows.Forms.Button();

              this.buttonDel = new System.Windows.Forms.Button();

              this.textBoxAdd = new System.Windows.Forms.TextBox();

              this.SuspendLayout();

              this.buttonOK.Location = new System.Drawing.Point(188, 108);

              this.buttonOK.Name = "buttonOK";

              this.buttonOK.TabIndex = 0;

              this.buttonOK.Text = "確定";

              this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);

              this.listBoxFrm2.ItemHeight = 12;

              this.listBoxFrm2.Location = new System.Drawing.Point(8, 8);

              this.listBoxFrm2.Name = "listBoxFrm2";

              this.listBoxFrm2.Size = new System.Drawing.Size(168, 124);

              this.listBoxFrm2.TabIndex = 2;

              this.buttonAdd.Location = new System.Drawing.Point(188, 44);

              this.buttonAdd.Name = "buttonAdd";

              this.buttonAdd.TabIndex = 3;

              this.buttonAdd.Text = "增加";

              this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click);

              this.buttonDel.Location = new System.Drawing.Point(188, 76);

              this.buttonDel.Name = "buttonDel";

              this.buttonDel.TabIndex = 4;

              this.buttonDel.Text = "刪除";

              this.buttonDel.Click += new System.EventHandler(this.buttonDel_Click);

              this.textBoxAdd.Location = new System.Drawing.Point(188, 12);

              this.textBoxAdd.Name = "textBoxAdd";

              this.textBoxAdd.Size = new System.Drawing.Size(76, 21);

              this.textBoxAdd.TabIndex = 5;

              this.textBoxAdd.Text = "";

              this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);

              this.ClientSize = new System.Drawing.Size(272, 141);

              this.Controls.Add(this.textBoxAdd);

              this.Controls.Add(this.buttonDel);

              this.Controls.Add(this.buttonAdd);

              this.Controls.Add(this.listBoxFrm2);

              this.Controls.Add(this.buttonOK);

              this.Name = "Form2";

              this.Text = "Form2";

              this.ResumeLayout(false);

         }

         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窗體,其實在兩個不同的實例間交互數據,都可以採用三篇文章中的方案實現,除非是這個類特有的屬性或着方法。現在都講完了,雖然不是什麼高深的東西,但是希望能對一些初學者有所幫助,要是能真正的能解決一些朋友的實際問題,也算是我沒有浪費時間來寫文章,同時也歡迎各位朋友進行技術交流,共同提高,我的郵件地址[email protected]

本文實際轉載自:http://www.zahui.com/html/14/34053.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章