老調重彈 -- 再談 Windows Form 程式視窗互相傳值(1)

這算是個老掉牙的問題了,雖然題目叫『再談』,但事實上我從來沒有特別在這部落格上寫過這題目;不過這個主題很熱門,也有很多人寫過,而且他們寫的都還真不錯。比如像Larry的 [.NET Concept][C#][VB.NET].NET兩個表單間的資料互通 、 Jeff Yeh的 幾種WinForm的Form與Form之間的傳值方法;不勝枚舉,恕我就不一一列出了。

       這算是個老掉牙的問題了,雖然題目叫『再談』,但事實上我從來沒有特別在這部落格上寫過這題目;不過這個主題很熱門,也有很多人寫過,而且他們寫的都還真不錯。比如像Larry的 [.NET Concept][C#][VB.NET].NET兩個表單間的資料互通 、 Jeff Yeh的 幾種WinForm的Form與Form之間的傳值方法;不勝枚舉,恕我就不一一列出了。

 

       這些朋友們都寫得這麼好,那我還寫來幹嘛?雖然現在咱的腦袋已經不甚靈光,不過偶爾還是有點新花樣的。在這篇要談的是多對多兼亂七八糟的傳法,因為最近『設計模式』一詞甚為流行,所以為這亂七八糟的東西取了個響亮的名詞叫『亂七八糟設計模式』(千萬別問我正經的名詞叫什麼,因為壓根我就沒去記過那一堆設計模式的名字)。

 

       回歸正題,過去大家比較常用的解法多半是以傳控制項參考的到另一個Form的方式來解決這個問題。但我個性實在太愛瞎弄了,所以假想了一個狀況:如果有 Form1、Form2、Form3,而這三個Form都有可能會開啟FormA、FormB、FormC,而它們去更改呼叫者的控制項不一樣,甚至不是個控制項 (例如是用GDI+ 畫出字之類的),那還有沒有別種解法?我得說,解法事實上有很多種,這篇文講的也只是其中一種解法,單純只希望能引起看文的人有更多的想法去撰寫這類的程式碼而已。

 

       在正式開始之前,我得先做個小假設否則問題會滾越大,這個假設是在各個Form間要傳遞的只有一個String型別的玩意,也就是說咱們只傳字串,省得把問題弄得太過複雜。另外要提醒的是這篇的解法會用到介面 (Interface) 的觀念,如果你對介面完全沒概念可以參考MSDN文件 [介面 (Visual Basic)] 或 [介面 (C# 程式設計手冊)]。

 

       第一個狀況,兩個不同的Form (ChildForm1與ChildForm2) 去開啟同一個Form(SlaveForm1),配置如下:

       (1) ChildForm1:一個Button執行開啟SlaveForm1的功能,一個TextBox用來顯示SlaveForm1回傳的字串。

       (2) ChildForm2:一個Button執行開啟SlaveForm1的功能,一個Label用來顯示SlaveForm1回傳的字串。

       (3) SlaveForm1:一個Button執行回傳自己TextBox上所輸入字串到開啟SlaveForm1的Form上(有可能是(1)或(2)),一個TextBox用來輸入字串。

       一般我們可能會這麼做,就是把控制項的參考傳給SlaveForm1:

Public Class SlaveForm1
    Public Property upFormControl As Control
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        upFormControl.Text = TextBox1.Text
    End Sub
End Class
Public Class ChildForm1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim popForm As New SlaveForm1
        popForm.upFormControl = Me.TextBox1
        popForm.Show()
    End Sub
End Class
Public Class ChildForm2
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim popForm As New SlaveForm1
        popForm.upFormControl = Me.Label1
        popForm.Show()
    End Sub
End Class

       那『亂七八糟設計模式』要怎麼做?首先我們建立一個介面叫 ICaller,並給定一個WriteOnly Property (WriteOnly 只是為了讓範例單純化,因為範例中不需要讀取此屬性值,只要能寫入就好)。

Public Interface ICaller
    WriteOnly Property Passvalue As String
End Interface

       接著讓ChildForm1和ChildForm2都實作這個介面,以ChildForm1為例你會看到這樣的程式碼

Public Class ChildForm1
    Implements ICaller

    Public WriteOnly Property Passvalue As String Implements ICaller.Passvalue
        Set(ByVal value As String)

        End Set
    End Property

    

       接著咱們回到SlaveForm1,新增一個以ICaller為型別的屬性

 

Public Class SlaveForm1
    Public Property caller As ICaller
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        caller.Passvalue = TextBox1.Text
    End Sub
End Class

        套句91常講的話:『不管上面那一堆毛是什麼毛』,反正阿貓阿狗大毛二毛三毛小毛只要實作了ICaller介面,SlaveForm1就認的出來。這種應用介面的方式稱為多型 (囉唆的名詞叫介面架構多型) ,如果你想多瞭解一點多型可以看MSDN文件 [Visual Basic 程式設計手冊 多型]  或 [多型 (C# 程式設計手冊)] ,這表示 ChildForm1 與 ChildForm2 的型別除了它自己及它所繼承的上層(上到很遠的地方)類別外,它還有一個型別叫 ICaller ,只是當你用這個型別存取它的時候只有一個WriteOnly Passvalue屬性就是了。

      

      下一個步驟就是在ChildForm1與ChildForm2的 Passvalue屬性中搞鬼了,會完成如以下的內容

Public Class ChildForm1
    Implements ICaller

    Public WriteOnly Property Passvalue As String Implements ICaller.Passvalue
        Set(ByVal value As String)
            TextBox1.Text = value
        End Set
    End Property

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim popForm As New SlaveForm1
        popForm.caller = Me
        popForm.Show()
    End Sub

End Class
Public Class ChildForm2
    Implements ICaller

    Public WriteOnly Property Passvalue As String Implements ICaller.Passvalue
        Set(ByVal value As String)
            Label1.Text = value
        End Set
    End Property

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim popForm As New SlaveForm1
        popForm.caller = Me
        popForm.Show()
    End Sub

End Class

       看起來程式碼還多了一些,好像感覺更不理想喔?那是因為我舉的例子太單純,其實這個介面架構多型的應用方式在當你的需求變得更亂七八糟的時候就會發揮出它威力,能夠大大提高程式的彈性與可維護性。