C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

条件により類似のモーダルフォームを切り替える

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■87426 / inTopicNo.1)  条件により類似のモーダルフォームを切り替える
  
□投稿者/ うるち米 (1回)-(2018/05/21(Mon) 17:23:13)

分類:[VB.NET/VB2005 以降] 

現在、以下のようなコードでモーダルフォームを表示し、
モーダルフォームで設定した情報を取得し、これを元に処理をしています。
(VB2015使用)

Using f As New Form1
    If f.ShowDialog(Me) = DialogResult.Cancel Then
        Return
    End If
    
    Dim para1 as String =  f.Prop1
    Dim para2 as Integer =  f.Prop2
    Dim para3 as Boolean =  f.Prop3
    
    '(以下、これらパラメータを使用して処理)

End Using

ただし、今回の修正で、条件によりモーダルで開くフォームをForm1ではなくForm2にする、
ということを行う必要が出てきました。
ただし、Form1とForm2はデザインが異なるのみで、この処理に使用する各フォームのプロパティも
Prop1, Prop2, Prop3という同じ名前・同じ型です。

例えば、以下のようにすればよいかも知れませんが、Usingの内部は全く同じコードとなり、
1箇所にまとめてコードを書くことができればメンテナンスも楽ではないかと思います。

If (条件) Then
    Using f As New Form1
        If f.ShowDialog(Me) = DialogResult.Cancel Then
            Return
        End If
        
        Dim para1 as String =  f.Prop1
        Dim para2 as Integer =  f.Prop2
        Dim para3 as Boolean =  f.Prop3
        
        '(以下、これらパラメータを使用して処理)

    End Using
Else
    Using f As New Form2
        If f.ShowDialog(Me) = DialogResult.Cancel Then
            Return
        End If
        
        Dim para1 as String =  f.Prop1
        Dim para2 as Integer =  f.Prop2
        Dim para3 as Boolean =  f.Prop3
        
        '(以下、これらパラメータを使用して処理)

    End Using
End If

このような場合、どのようなコードを書くのが望ましいでしょうか?

引用返信 編集キー/
■87427 / inTopicNo.2)  Re[1]: 条件により類似のモーダルフォームを切り替える
□投稿者/ 魔界の仮面弁士 (1678回)-(2018/05/21(Mon) 17:53:47)
No87426 (うるち米 さん) に返信
> ただし、Form1とForm2はデザインが異なるのみで、この処理に使用する各フォームのプロパティも
> Prop1, Prop2, Prop3という同じ名前・同じ型です。
> このような場合、どのようなコードを書くのが望ましいでしょうか?

案1:インターフェイス実装
・Prop1〜Prop3 をメンバーに持つ「Public Interface IExampleDialog」を用意する。
・Form1 / Form2 は、この IExampleDialog を Implements する。
・利用する側は『Using f As IExampleDialog = 〜』とする。


案2:クラス継承
・Prop1〜Prop3 をメンバーに持つ「MustInherit Class IExampleBase」を用意する。
・Form1 / Form2 は、System.Windows.Forms.Form のかわりに、この IExampleBase を Inherits する。
・利用する側は『Using f As IExampleBase = 〜』とする。
引用返信 編集キー/
■87428 / inTopicNo.3)  Re[2]: 条件により類似のモーダルフォームを切り替える
□投稿者/ 魔界の仮面弁士 (1679回)-(2018/05/21(Mon) 18:03:37)
# パスワード入力をミスって訂正できなかったので、追記で。

No87427 (魔界の仮面弁士) に追記
> 案1:インターフェイス実装
> ・Prop1〜Prop3 をメンバーに持つ「Public Interface IExampleDialog」を用意する。

インターフェイス(Interface)には通常、大文字 I で始まる名前をつけます。


> 案2:クラス継承
> ・Prop1〜Prop3 をメンバーに持つ「MustInherit Class IExampleBase」を用意する。

上記はインターフェイスではなく抽象クラスなので、先頭の I は不自然ですね。

Class IExampleBase ではなく
Class ExampleBase に訂正しておいてください。
引用返信 編集キー/
■87437 / inTopicNo.4)  Re[3]: 条件により類似のモーダルフォームを切り替える
□投稿者/ うるち米 (2回)-(2018/05/22(Tue) 10:01:36)
ありがとうございます。
とりあえず、Interfaceの方法を試してみました。
ただし、『Using f As IExampleDialog = 〜』のような使い方をしようとすると、

 'Using' オペランドは System.IDisposable を実装しなければなりません。

という文法エラーが発生します。

そこで、色々と考えたのですが、Form1もForm2もFormなので、
『Using f As IExampleDialog = 〜』のところを
『Using f As Form = 〜』のようにして、
インタフェースのメンバを参照する際にIExampleDialog にキャストしたものを用意する、
というようにしてみたのですが、これは正しいやり方でしょうか?
とりあえず動いているみたいなのですが、自身がありません。
引用返信 編集キー/
■87440 / inTopicNo.5)  Re[4]: 条件により類似のモーダルフォームを切り替える
□投稿者/ 魔界の仮面弁士 (1680回)-(2018/05/22(Tue) 13:41:40)
No87437 (うるち米 さん) に返信
>  'Using' オペランドは System.IDisposable を実装しなければなりません。

あー…。すみません。

案1 の場合、たとえばこんな実装になります。
VB のバージョンにもよりますけど。

Using f As IExampleDialog = New Form1
 If f.ShowDialog(Me) <> DialogResult.Cancel Then
  MsgBox(f.Prop1)
 End If
End Using


Public Interface IExampleDialog
  Inherits Global.System.IDisposable
  Function ShowDialog(owner As Global.System.Windows.Forms.IWin32Window) As Global.System.Windows.Forms.DialogResult
  Property Prop1 As String
  以下略
End Interface

Public Class Form1
  Inherits Global.System.Windows.Forms.Form
  Implements IExampleDialog
  Private Function IDialog_ShowDialog(owner As Global.System.Windows.Forms.IWin32Window) As DialogResult Implements IExampleDialog.ShowDialog
    Return MyBase.ShowDialog(owner)
  End Function
  Public Property Prop1 As String Implements IExampleDialog.Prop1
  以下略
End Class


案 2 の方は

 Public MustInherit Class DialogBase
  Inherits Global.System.Windows.Forms.Form
  Public MustOverride Property Prop1 As String
  Public MustOverride Property Prop2 As Integer
  Public MustOverride Property Prop3 As Boolean
 End Class

を用意しておいて、Form1 / Form2 がこの DialogBase を Inherits するという形になります。


> インタフェースのメンバを参照する際にIExampleDialog にキャストしたものを用意する、
その場合、IExampleDialog を実装していないフォームに備えて、
TryCast 呼びにする必要がありそうです。
引用返信 編集キー/
■87461 / inTopicNo.6)  Re[5]: 条件により類似のモーダルフォームを切り替える
□投稿者/ うるち米 (3回)-(2018/05/24(Thu) 17:00:53)
ありがとうございます。
また、ご連絡が遅くなり申し訳ございません。
教えて頂いた方法でうまくいきました。
※ただし、Interfaceを実装したクラス側に書いた以下のPrivateメソッドが外部から呼び出される仕組みがまだ理解できていません。
 Private Function IDialog_ShowDialog(owner As Global.System.Windows.Forms.IWin32Window) As DialogResult Implements IExampleDialog.ShowDialog

今度、案2(抽象クラスというのですね)についても試してみようと思います。
解決済み
引用返信 編集キー/
■87464 / inTopicNo.7)  Re[6]: 条件により類似のモーダルフォームを切り替える
□投稿者/ Jitta (375回)-(2018/05/24(Thu) 22:58:41)
No87461 (うるち米 さん) に返信

Using を使わなければならない、ということはないのだから、弁士さんの最初の案1で、


Dim f As IExampleDialog = Nothing
Try
  If (条件) Then
  f = New Form1 ← この Form1 は IExampleDialog を継承していること
  Else If 条件 Then
    f = New Form2 ← 同じく
  End If  
  If f.ShowDialog(Me) = DialogResult.Cancel Then
Return
  End If

  Dim para1 as String = f.Prop1
  Dim para2 as Integer = f.Prop2
  Dim para3 as Boolean = f.Prop3

Finally
  f?.Dispose
  Form にキャストしてからかな?
End Try


解決済み
引用返信 編集キー/
■87469 / inTopicNo.8)  Re[6]: 条件により類似のモーダルフォームを切り替える
□投稿者/ 魔界の仮面弁士 (1685回)-(2018/05/25(Fri) 10:24:48)
No87461 (うるち米 さん) に返信
> ※ただし、Interfaceを実装したクラス側に書いた以下のPrivateメソッドが外部から呼び出される仕組みがまだ理解できていません。
>  Private Function IDialog_ShowDialog(owner As Global.System.Windows.Forms.IWin32Window) As DialogResult Implements IExampleDialog.ShowDialog

大雑把な解説になりますが:


呼び出す際には、『f.ShowDialog(Me)』というコードだったわけですよね。

このとき、変数 f は IExampleDialog 型としていますから、これは
「IExampleDialog インターフェイスの ShowDialog メソッド」を
通じて呼び出していることになります。
このメソッドは、インターフェイスで公開されたものであるため、
呼び出し元からでも問題なくアクセスすることができます。


一方、変数 f で保持されているインスタンスは、Form1/Form2 型のオブジェクトでしたので、
最終的には、各クラスで「Implements IExampleDialog.ShowDialog」されたメソッド実装が呼ばれます。

この実装先のメソッドのスコープは、実際には Public でも Private でも構いませんし、メソッド名も
IExampleDialog_ShowDialog だろうと IDialog_ShowDialog だろうと、どんな名前でも自由です。

あるいは Private Function IDialog_ShowDialog ではなく、
Public Function ShowDialog のままで Implements させることもできます。
ただしその場合、元のメソッド名と被ってしまうので、実装時には
 Public Overloads Function ShowDialog(owner As IWin32Window) As DialogResult Implements IExampleDialog.ShowDialog
もしくは
 Public Shadows Function ShowDialog(owner As IWin32Window) As DialogResult Implements IExampleDialog.ShowDialog
の形式を取る必要があります。



> 今度、案2(抽象クラスというのですね)についても試してみようと思います。

個人的には 案1 がお奨めです。

案1 の実装の場合、呼び出し側で利用できるのは、キャストしない限りは
「IExampleDialog に実装されたメソッド」に限定されます。
ということは、コーディング時に f.何某 で呼び出される IntelliSense に表示されるメンバーも、
必要最低限なものだけが浮き彫りになるので、利用側としてもわかりやすくなると思います。

しかし 案2 だと、呼び出し元では必要としない、素の Form クラスのメンバーまで表示されてしまいます。
それが必要な場合もありますし、共通で実装しなければならない機能が多い場合は、
案2 の方が実装しやすいケースもありますので、使い分けということにはなりますけれどね。



それと、今回の本題である『条件により類似のモーダルフォームを切り替える』という目的のために、
案1/案2 では、「f As IExampleDialog = New Form1()」のようにして、インスタンスの型と
それを受けとる変数の型にズレが生じる結果となっています。

これが分かり難いという場合には、ヘルパーととなる Factory を用意しておくと便利です。

 Using f = DialogFactorySample.CreateDialog(…)
  If f.ShowDialog(Me) = DialogResult.Cancel Then
   Return
  End If
  :
 End Using


上記の場合、DialogFactorySample クラスはたとえば

 Public Shared Function CreateDialog(…) As IExampleDialog
  If 条件1 Then
   Return New Form1()
  ElseIf 条件2 Then
   Return New Form2()
  Else
   :
  End If
 End Function

のような実装をイメージしています。(Shared にするかどうかはお好みで)



ついでに言えば、Dispose 処理もカプセル化してしまって、
呼び出し側で Using を呼ぶ手間も省いてしまうのも良いかと思います。

つまり VB の InputBox 関数のような実装にして、ダイアログの結果を
メソッドの戻り値だけで表現するようにする、ということです。


[案3]
'呼び出す側
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  Dim result = Form2.ShowModalDialog(Me)
  If result Is Nothing Then
    Label1.Text = "Cancel された"
  Else
    Label1.Text = result.Prop1
  End If
End Sub


'呼ばれる側
Public Class Form2
  Public Shared Function ShowModalDialog(owner As IWin32Window) As ExampleDialogResult
    Using dlg As New Form2()
      If dlg.ShowDialog(owner) = DialogResult.Cancel Then
        Return Nothing
      Else
        Return New ExampleDialogResult("VB", 2017, True)
      End If
    End Using
  End Function

  'コンストラクタを非公開にして、外部から生成できなくしてしまう
  Private Sub New()
    InitializeComponent()
  End Sub
End Class

'返却値のためのクラス(こういったものを作るのが面倒なら、タプルで代用しても良い)
Public Class ExampleDialogResult
  Public ReadOnly Property Prop1 As String
  Public ReadOnly Property Prop2 As Integer
  Public ReadOnly Property Prop3 As Boolean
  Public Sub New(s As String, i As Integer, b As Boolean)
    Prop1 = s
    Prop2 = i
    Prop3 = b
  End Sub
End Class


上記の 案3 は、当初の「条件により類似のモーダルフォームを切り替える」という
目的からは離れてしまうので、仮に上記のように Dispose まで内包させるとしても、
多態性(polymorphism)を実装するために、案1/案2 のように
共通部分となるインターフェイスやクラスは用意した方が良いとは思います。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ