none
ボタンを押したときに、直前にアクティブだったコントロールを取得するには? RRS feed

  • 質問

  • テキストボックスやチェックボックスやオプションボタンなど、いくつものコントロールが配置されたフォームがあります。
    フォームにはボタンもあり、そのボタンをクリックすると、直前までアクティブだったコントロールがテキストボックスかどうか調べ、それがテキストボックスなら、そこへ特定の文字を追記するプログラムを作りたいと思っています。

    今現在アクティブなコントロールはActiveControlで取得できますが、ボタンを押した時点でボタンとなってしまい、意図した動作が行えません。
    ボタンではなく、ラベルをボタンのようにみせかければうまくいくのですが、そういうトリッキーなこともしたくはありません。
    各コントロールのLostFocusイベントを受け取ればいいのでしょうが、コントロールが多すぎてそれもやりたくありません。

    何かいい方法はありますでしょうか?
    よろしくお願いします。

    2020年5月8日 8:11

回答

  • こんな

    Public Class Form1
        Private WithEvents buttonEx1 As ButtonEx
    
        Sub New()
            InitializeComponent()
    
            Dim y As Integer
            For i = 0 To 3
                Dim txb As New TextBox
                txb.Top = y
    
                Me.Controls.Add(txb)
                y += txb.Height + 5
            Next
    
            buttonEx1 = New ButtonEx()
            buttonEx1.Top = y
            buttonEx1.Text = "てすと"
            Me.Controls.Add(buttonEx1)
        End Sub
    
        Private Sub ButtonEx1_Click(sender As Object, e As EventArgs) Handles buttonEx1.Click
            Dim btn = CType(sender, ButtonEx)
            If TypeOf btn.PreviousControl Is TextBox Then
                Dim txb = CType(btn.PreviousControl, TextBox)
                txb.Text = DateTime.Now.ToString("HH:mm:ss")
            End If
        End Sub
    End Class
    
    
    Class ButtonEx
        Inherits Button
    
        Public ReadOnly Property PreviousControl As Control
            Get
                If _Prev Is Nothing Then
                    Return Nothing
                End If
                Dim c As Control = Nothing
                If Not _Prev.TryGetTarget(c) Then
                    _Prev = Nothing
                End If
                Return c
            End Get
        End Property
        Private _Prev As WeakReference(Of Control)
    
        Protected Overrides Sub OnLostFocus(e As EventArgs)
            _Prev = Nothing
            MyBase.OnLostFocus(e)
        End Sub
        Protected Overrides Sub OnGotFocus(e As EventArgs)
            MyBase.OnGotFocus(e)
        End Sub
    
        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case &H7 'WM_SETFOCUS
                    Dim parent As Control
                    parent = Me.Parent
                    Do While parent IsNot Nothing
                        If TypeOf parent Is Form Then
                            Dim f As Form = parent
                            If f.ActiveControl Is Me Then
                                _Prev = Nothing
                            Else
                                _Prev = New WeakReference(Of Control)(f.ActiveControl)
                                Exit Do
                            End If
                        End If
                        parent = parent.Parent
                    Loop
                Case Else
            End Select
            MyBase.WndProc(m)
        End Sub
    End Class

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

    2020年5月8日 9:23
  • 要するに 「(Of 型パラメーター)」が無い方のクラスに置き換えるという事です。
    コードを修正すると、こんな感じになると思います。※試していません

    Class ButtonEx
        Inherits Button
    
        Public ReadOnly Property PreviousControl As Control
            Get
                If _Prev Is Nothing Then
                    Return Nothing
                End If
                'Dim c As Control = Nothing
                'If Not _Prev.TryGetTarget(c) Then
                '    _Prev = Nothing
                'End If
                'Return c
                Return TryCast(_Prev.Target, Control)
            End Get
        End Property
        'Private _Prev As WeakReference(Of Control)
        Private _Prev As WeakReference
    
        Protected Overrides Sub OnLostFocus(e As EventArgs)
            _Prev = Nothing
            MyBase.OnLostFocus(e)
        End Sub
        Protected Overrides Sub OnGotFocus(e As EventArgs)
            MyBase.OnGotFocus(e)
        End Sub
    
        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case &H7 'WM_SETFOCUS
                    Dim parent As Control
                    parent = Me.Parent
                    Do While parent IsNot Nothing
                        If TypeOf parent Is Form Then
                            'Dim f As Form = parent
                            Dim f As Form = DirectCast(parent, Form)
                            If f.ActiveControl Is Me Then
                                _Prev = Nothing
                            Else
                                '_Prev = New WeakReference(Of Control)(f.ActiveControl)
                                _Prev = New WeakReference(f.ActiveControl)
                                Exit Do
                            End If
                        End If
                        parent = parent.Parent
                    Loop
                Case Else
            End Select
            MyBase.WndProc(m)
        End Sub
    End Class

    2020年5月12日 5:54

すべての返信

  • こんな

    Public Class Form1
        Private WithEvents buttonEx1 As ButtonEx
    
        Sub New()
            InitializeComponent()
    
            Dim y As Integer
            For i = 0 To 3
                Dim txb As New TextBox
                txb.Top = y
    
                Me.Controls.Add(txb)
                y += txb.Height + 5
            Next
    
            buttonEx1 = New ButtonEx()
            buttonEx1.Top = y
            buttonEx1.Text = "てすと"
            Me.Controls.Add(buttonEx1)
        End Sub
    
        Private Sub ButtonEx1_Click(sender As Object, e As EventArgs) Handles buttonEx1.Click
            Dim btn = CType(sender, ButtonEx)
            If TypeOf btn.PreviousControl Is TextBox Then
                Dim txb = CType(btn.PreviousControl, TextBox)
                txb.Text = DateTime.Now.ToString("HH:mm:ss")
            End If
        End Sub
    End Class
    
    
    Class ButtonEx
        Inherits Button
    
        Public ReadOnly Property PreviousControl As Control
            Get
                If _Prev Is Nothing Then
                    Return Nothing
                End If
                Dim c As Control = Nothing
                If Not _Prev.TryGetTarget(c) Then
                    _Prev = Nothing
                End If
                Return c
            End Get
        End Property
        Private _Prev As WeakReference(Of Control)
    
        Protected Overrides Sub OnLostFocus(e As EventArgs)
            _Prev = Nothing
            MyBase.OnLostFocus(e)
        End Sub
        Protected Overrides Sub OnGotFocus(e As EventArgs)
            MyBase.OnGotFocus(e)
        End Sub
    
        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case &H7 'WM_SETFOCUS
                    Dim parent As Control
                    parent = Me.Parent
                    Do While parent IsNot Nothing
                        If TypeOf parent Is Form Then
                            Dim f As Form = parent
                            If f.ActiveControl Is Me Then
                                _Prev = Nothing
                            Else
                                _Prev = New WeakReference(Of Control)(f.ActiveControl)
                                Exit Do
                            End If
                        End If
                        parent = parent.Parent
                    Loop
                Case Else
            End Select
            MyBase.WndProc(m)
        End Sub
    End Class

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

    2020年5月8日 9:23
  • ありがとうございます。
    やろうとしましたがWeakReferenceで以下のエラーが出てしまいます。
    エラー BC32045 'WeakReference' は型パラメーターが指定されてないため、型引数を指定することができません。
    2020年5月10日 23:53
  • ジェネリックな WeakReference は .NET 4.5 以降なので、それより前の場合は WeakReference に読み替えてください。
    2020年5月11日 11:04
  • 言いたいことはわからないでもないですが、最低限、文章を書きませんか?
    フォーラムはあなたのためのサポートではありません。相手とのコミュニケーションの場なので、ちゃんと文章を書きましょう。
    (プラス、努力した痕跡を見せることが相手に対する誠意だと思いますので、ご参考まで)
    2020年5月12日 3:16
    モデレータ
  • 失礼しました。
    「WeakReference を WeakReference に読み替えてください」
    というのが分かりません。
    2020年5月12日 4:10
  • 失礼しました。
    「WeakReference を WeakReference に読み替えてください」
    というのが分かりません。

    「『ジェネリックな WeakReference』を『非ジェネリックな WeakReference』に読み替える」 ということですよ。

    2020年5月12日 5:45
  • 「ジェネリックな」WeakReference というのは

    「WeakReference<T> クラス」
    https://docs.microsoft.com/ja-jp/dotnet/api/system.weakreference-1

    のことで、WeakReference というのは

    「WeakReference クラス」
    https://docs.microsoft.com/ja-jp/dotnet/api/system.weakreference

    のことです。

    エラーメッセージからみて .NET 4.5 より前のフレームワークでコンパイルしてるのではと思いましたが違いますか?

    2020年5月12日 5:50
  • 要するに 「(Of 型パラメーター)」が無い方のクラスに置き換えるという事です。
    コードを修正すると、こんな感じになると思います。※試していません

    Class ButtonEx
        Inherits Button
    
        Public ReadOnly Property PreviousControl As Control
            Get
                If _Prev Is Nothing Then
                    Return Nothing
                End If
                'Dim c As Control = Nothing
                'If Not _Prev.TryGetTarget(c) Then
                '    _Prev = Nothing
                'End If
                'Return c
                Return TryCast(_Prev.Target, Control)
            End Get
        End Property
        'Private _Prev As WeakReference(Of Control)
        Private _Prev As WeakReference
    
        Protected Overrides Sub OnLostFocus(e As EventArgs)
            _Prev = Nothing
            MyBase.OnLostFocus(e)
        End Sub
        Protected Overrides Sub OnGotFocus(e As EventArgs)
            MyBase.OnGotFocus(e)
        End Sub
    
        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case &H7 'WM_SETFOCUS
                    Dim parent As Control
                    parent = Me.Parent
                    Do While parent IsNot Nothing
                        If TypeOf parent Is Form Then
                            'Dim f As Form = parent
                            Dim f As Form = DirectCast(parent, Form)
                            If f.ActiveControl Is Me Then
                                _Prev = Nothing
                            Else
                                '_Prev = New WeakReference(Of Control)(f.ActiveControl)
                                _Prev = New WeakReference(f.ActiveControl)
                                Exit Do
                            End If
                        End If
                        parent = parent.Parent
                    Loop
                Case Else
            End Select
            MyBase.WndProc(m)
        End Sub
    End Class

    2020年5月12日 5:54
  • うまくいきました。ありがとうございました。

    それにしても、クラスを追加しないとできないものなんですね。
    もっと簡単にできるかものと思ってました。。。

    2020年5月12日 6:31