none
指定した文字列で型宣言をしたい RRS feed

  • 質問

  • VisualStudio2019(Ver16.1.6)でアプリケーションを作成しています。
    Windowsフォームを複数作成しており、各フォームを開く際の処理を共通化したいと考えています。
    親フォーム型…TopForm
    子フォーム1型…ChildForm1
    子フォーム2型…ChildForm2
    TopFormには、Button1とButton2を配置しており、
    それぞれのボタンを押したときに、各子フォームを開く仕様です。
    TopForm.vb内のプログラムは、以下のとおりです。
    'Button1クリックイベント
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim f As New ChildForm1
        ShowForm(f)
    End Sub
    'Button2クリックイベント
    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim f As New ChildForm2
        ShowForm(f)
    End Sub
    '子フォームを開く
    Private Sub ShowForm(ByVal f As Form)
        Me.Hide()
        f.ShowDialog(Me)
        f.Dispose()
        Me.Show()
    End Sub
    このボタンのクリックイベントの処理を共通化したく、
    各子フォームの型名を文字列の配列で宣言しておき、その文字列で型宣言をしたいのですが、可能でしょうか。
    実現したいイメージは、以下のようなものです。
    '子フォームの型名
    Private ReadOnly cFormTypeNameTbl() = {
        "ChildForm1",
        "ChildForm2"
    }
    'Buttonクリックイベント
    Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click,Handles Button2.Click
        Dim tmpBtn as Button = sender
        'TagプロパティにはcFormTypeNameTblのインデックスを登録している前提
        'Button1.Tag=0
        'Button2.Tag=1
        ShowForm(cFormTypeNameTbl(CInt(tmpBtn.Tag))
    End Sub
    '子フォームを開く
    Private Sub ShowForm(ByVal formName As String)
        Dim f As New formName 'ここの対処が不明
        Me.Hide()
        f.ShowDialog(Me)
        f.Dispose()
        Me.Show()
    End Sub
    以上のようなプログラミングをすると
    当たり前ですが、ShowForm()の「Dim f As New formName」でエラーが出ます。
    「エラー BC30002 型 'formName' は定義されていません。」
    何かしら手を加える必要があることは理解していますが、
    その手法が分からず、そもそも可能なのかご教示のほどお願いいたします。

    2019年8月16日 9:40

回答

  • いちおう型名からTypeを調べてActivator.CreateInstanceでインスタンスは作れますが、名前空間までを考えると面倒…

    Public Class Form1
        Inherits Form
        Friend WithEvents Button1 As Button
        Friend WithEvents Button2 As Button
    
        Sub New()
            Button1 = New Button() With {.Tag = 0, .Text = "1"}
            Button2 = New Button() With {.Tag = 1, .Text = "2", .Top = Button1.Height + 10}
    
            Me.Controls.Add(Button1)
            Me.Controls.Add(Button2)
        End Sub
    
        '子フォームの型名
        Private ReadOnly cFormTypeNameTbl() = {"ChildForm1", "ChildForm2"}
    
        'Buttonクリックイベント
        Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click, Button2.Click
            Dim tmpBtn As Button = sender
            'TagプロパティにはcFormTypeNameTblのインデックスを登録している前提
            'Button1.Tag=0
            'Button2.Tag=1
            ShowForm(cFormTypeNameTbl(CInt(tmpBtn.Tag)))
        End Sub
    
        '子フォームを開く
        Private Sub ShowForm(ByVal formName As String)
    
            Dim fullName As String = Me.GetType().Namespace + "." + formName
            Dim formType As Type = Type.GetType(fullName)
    
            Dim f = CType(System.Activator.CreateInstance(formType), Form)
    
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class

    それよりは文字列配列でなくType型の配列にしておいた方が楽です

    Public Class Form2
        Inherits Form
    
        Friend WithEvents Button1 As Button
        Friend WithEvents Button2 As Button
    
        Sub New()
            Button1 = New Button() With {.Tag = 0, .Text = "1"}
            Button2 = New Button() With {.Tag = 1, .Text = "2", .Top = Button1.Height + 10}
    
            Me.Controls.Add(Button1)
            Me.Controls.Add(Button2)
        End Sub
    
        '子フォームの型名
        Private ReadOnly cFormTypeNameTbl() As Type = {GetType(ChildForm1), GetType(ChildForm2)}
    
        'Buttonクリックイベント
        Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click, Button2.Click
            Dim tmpBtn As Button = sender
            'TagプロパティにはcFormTypeNameTblのインデックスを登録している前提
            'Button1.Tag=0
            'Button2.Tag=1
            ShowForm(cFormTypeNameTbl(CInt(tmpBtn.Tag)))
        End Sub
    
        '子フォームを開く
        Private Sub ShowForm(ByVal formType As Type)
            Dim f As Form = CType(System.Activator.CreateInstance(formType), Form)
    
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class

    #すでに回答がついてるようなデリゲートを用意して、それをTagに設定しておくほうが楽ですが


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク ssssssssssf 2019年8月16日 12:27
    2019年8月16日 10:18

すべての返信

  • たとえばこういうのはどうでしょう。

    Button1.Tag には "VB"、Button2.Tag には "C#" が格納されているものとします。

    Option Strict On
    Imports System.Collections.ObjectModel
    
    Public Class TopForm
        Private cFormTypeNameTbl As ReadOnlyDictionary(Of Object, Func(Of Form))
    
        Private Sub TopForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            cFormTypeNameTbl = New ReadOnlyDictionary(Of Object, Func(Of Form))(
                New Dictionary(Of Object, Func(Of Form)) From {
                    {"VB", Function() New ChildForm1()},
                    {"C#", Function() New ChildForm2()}
                })
        End Sub
    
        Private Sub Buttons_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
            Dim key = DirectCast(sender, Control).Tag
            ShowForm(cFormTypeNameTbl(key)())
        End Sub
    
        Private Sub ShowForm(ByVal f As Form)
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class
    
    2019年8月16日 10:03
  • いちおう型名からTypeを調べてActivator.CreateInstanceでインスタンスは作れますが、名前空間までを考えると面倒…

    Public Class Form1
        Inherits Form
        Friend WithEvents Button1 As Button
        Friend WithEvents Button2 As Button
    
        Sub New()
            Button1 = New Button() With {.Tag = 0, .Text = "1"}
            Button2 = New Button() With {.Tag = 1, .Text = "2", .Top = Button1.Height + 10}
    
            Me.Controls.Add(Button1)
            Me.Controls.Add(Button2)
        End Sub
    
        '子フォームの型名
        Private ReadOnly cFormTypeNameTbl() = {"ChildForm1", "ChildForm2"}
    
        'Buttonクリックイベント
        Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click, Button2.Click
            Dim tmpBtn As Button = sender
            'TagプロパティにはcFormTypeNameTblのインデックスを登録している前提
            'Button1.Tag=0
            'Button2.Tag=1
            ShowForm(cFormTypeNameTbl(CInt(tmpBtn.Tag)))
        End Sub
    
        '子フォームを開く
        Private Sub ShowForm(ByVal formName As String)
    
            Dim fullName As String = Me.GetType().Namespace + "." + formName
            Dim formType As Type = Type.GetType(fullName)
    
            Dim f = CType(System.Activator.CreateInstance(formType), Form)
    
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class

    それよりは文字列配列でなくType型の配列にしておいた方が楽です

    Public Class Form2
        Inherits Form
    
        Friend WithEvents Button1 As Button
        Friend WithEvents Button2 As Button
    
        Sub New()
            Button1 = New Button() With {.Tag = 0, .Text = "1"}
            Button2 = New Button() With {.Tag = 1, .Text = "2", .Top = Button1.Height + 10}
    
            Me.Controls.Add(Button1)
            Me.Controls.Add(Button2)
        End Sub
    
        '子フォームの型名
        Private ReadOnly cFormTypeNameTbl() As Type = {GetType(ChildForm1), GetType(ChildForm2)}
    
        'Buttonクリックイベント
        Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click, Button2.Click
            Dim tmpBtn As Button = sender
            'TagプロパティにはcFormTypeNameTblのインデックスを登録している前提
            'Button1.Tag=0
            'Button2.Tag=1
            ShowForm(cFormTypeNameTbl(CInt(tmpBtn.Tag)))
        End Sub
    
        '子フォームを開く
        Private Sub ShowForm(ByVal formType As Type)
            Dim f As Form = CType(System.Activator.CreateInstance(formType), Form)
    
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class

    #すでに回答がついてるようなデリゲートを用意して、それをTagに設定しておくほうが楽ですが


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク ssssssssssf 2019年8月16日 12:27
    2019年8月16日 10:18
  • いっそのこと、Button.Tagプロパティには型名を入れておいてはどうでしょうか?

    Button1.Tag = GetType(ChildForm1).FullName

    2019年8月16日 10:23
  • 魔界の仮面弁士さん

    ご返信ありがとうございます。

    ReadOnlyDictionaryが、

    型ReadOnlyDictionaryは定義されていません。

    とエラーが出て、上手く使用できませんでした。。

    System.ObjectModelのインストールも失敗したのですが、

    私がVisualStudioをあまり理解できていないため、これまた解決策がわかりませんでした。

    折角教えていただいたのに、ごめんなさい。

    ただ、やりたかった内容は、コーディングいただいた内容です。

    まだまだ勉強中ですので、ReadOnlyDictionaryは、覚えておきます。

    ありがとうございました。

    2019年8月16日 11:35
  • こういう手もあります。

    Option Strict Off
    Public Class TopForm
        Private Sub TopForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Button1.Tag = Function() New ChildForm1()
            Button2.Tag = Function() New ChildForm2()
        End Sub
    
        Private Sub Buttons_Click(sender As Button, e As EventArgs) Handles Button1.Click, Button2.Click
            Dim f As Form = sender.Tag.Invoke()
            ShowForm(f)
        End Sub
    
        Private Sub ShowForm(ByVal f As Form)
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class
    2019年8月16日 11:37
  • gekkaさん

    GetTypeでテーブルを登録しておく案で実現できました。

    System.Activator.CreateInstanceの存在を知らなかったため、覚えておきます。

    勉強します。

    ありがとうございました。

    2019年8月16日 11:40
  • さらに、こういう手もあります。

    Option Strict Off
    Public Class TopForm
        Private Sub TopForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ’デザイン時に指定しておいても OK
            Button1.Tag = "ChildForm1"
            Button2.Tag = "ChildForm2"
        End Sub
    
        Private Sub Buttons_Click(sender As Button, e As EventArgs) Handles Button1.Click, Button2.Click
            Dim f As Form = CallByName(My.Forms, sender.Tag, CallType.Get)
            ShowForm(f)
        End Sub
    
        Private Sub ShowForm(ByVal f As Form)
            Me.Hide()
            f.ShowDialog(Me)
            f.Dispose()
            Me.Show()
        End Sub
    End Class


    • 編集済み 魔界の仮面弁士MVP 2019年8月16日 11:53 CallType.Method から CallType.Get に変更 (Method でも動くけど)
    2019年8月16日 11:50
  • 佐祐理さん

    たしかにTagにGetTypeでTypeを登録していれば、

    テーブルを定義する必要がなくなりました。

    Tagに登録するようにいたします。

    ありがとうございました。

    2019年8月16日 12:09
  • 魔界の仮面弁士さん

    なるほど。

    確かに、これもシンプルにできますね。

    ありがとうございます。

    2019年8月16日 12:20