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

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

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

キーボードを押した時に二重実行を防止するには

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

■92988 / inTopicNo.1)  キーボードを押した時に二重実行を防止するには
  
□投稿者/ けーえんす (1回)-(2019/11/12(Tue) 15:47:23)

分類:[.NET 全般] 

キーボードの矢印キーを押すと、Trackbarが移動するとともに、ある重い処理が実行されるプログラムを書いています。
一回ずつキーを押した場合には問題ないのですが、
矢印キーを押しっぱなしにした時に、キーを離してもしばらく処理が何度も実行されるという現象が起きています。

これを回避するために、ブーリアン変数で排他的に処理できるようにしてあるのですが、
それでもやはりうまくいきません、


コードは以下の通りです。

恐らく、2回目以降のキーの実行はArrowKeyMovementが実行される前の段階でキュー待ちような状態になっているのではないかと思います。


どうすれば良いでしょうか?


  AddHandler TrackBar1.KeyDown, AddressOf CursorMovementKeyDown





    Private Sub CursorMovementKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) 

        e.Handled = ArrowKeyMovement(e.KeyCode)

    End Sub

 Dim FrameChangingBool As Boolean = False


    Function ArrowKeyMovement(keyCode As Keys) As Boolean

        If FrameChangingBool = False Then

            FrameChangingBool = True

            Select Case keyCode
                Case Keys.Left

                    Dim SetValue As Integer = TrackBar1.Value - 1

                    If SetValue >= TrackBar1.Minimum Then

                        TrackBar1.Value = SetValue

                    End If

                    FrameChangingBool = False

                    Return True
                Case Keys.Right

                    Dim SetValue As Integer = TrackBar1.Value + 1

                    If SetValue <= TrackBar1.Maximum Then

                        TrackBar1.Value = SetValue

                    End If

                    FrameChangingBool = False
                    Return True

                Case Else
                    FrameChangingBool = False
                    Return False
            End Select

        Else

            Select Case keyCode
                Case Keys.Left, Keys.Right  
                    Return True
                Case Else
                    Return False
            End Select

        End If

    End Function



    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged

		’ 重い処理


    End Sub

引用返信 編集キー/
■92995 / inTopicNo.2)  Re[1]: キーボードを押した時に二重実行を防止するには
□投稿者/ ぼーちゃん (15回)-(2019/11/12(Tue) 17:51:20)
ValueChangedイベントハンドラ内でValueChangedイベントハンドラをRemove
KeyUpイベントハンドラ内でAddするのはどうですか


    'ValueChanged, KeyDown, KeyUpイベントハンドラはデザイナから自動生成

    Private KeyDownCancel As Boolean = False

    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged
        Console.WriteLine("ValueChanged")
        RemoveHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
        Console.WriteLine("重い処理")
    End Sub

    Private Sub TrackBar1_KeyDown(sender As Object, e As KeyEventArgs) Handles TrackBar1.KeyDown
        Console.WriteLine("KeyDown")
        e.Handled = KeyDownCancel
        KeyDownCancel = True
    End Sub

    Private Sub TrackBar1_KeyUp(sender As Object, e As KeyEventArgs) Handles TrackBar1.KeyUp
        Console.WriteLine("KeyUp")
        AddHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
        KeyDownCancel = False
    End Sub

引用返信 編集キー/
■92997 / inTopicNo.3)  Re[1]: キーボードを押した時に二重実行を防止するには
□投稿者/ KOZ (29回)-(2019/11/13(Wed) 05:53:43)
No92988 (けーえんす さん) に返信
> 恐らく、2回目以降のキーの実行はArrowKeyMovementが実行される前の段階でキュー待ちような状態になっているのではないかと思います。

メッセージループを止めるとキーボードを押した等のメッセージはキューに貯まります。
ループが再開する前にメッセージを PeekMessage で刈り取るといいかと。

Imports System.Threading
Imports System.Runtime.InteropServices

Public Class Form1

    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged
        Debug.Print("重い処理開始")
        Thread.Sleep(10000)
        Debug.Print("重い処理終了")
        CancelInput()
    End Sub

    <StructLayout(LayoutKind.Sequential)>
    Private Structure MSG
        Public hwnd As IntPtr
        Public Msg As Integer
        Public WParam As IntPtr
        Public LParam As IntPtr
        Public time As Integer
        Public x As Integer
        Public y As Integer
    End Structure

    Private Const WM_KEYFIRST As Integer = &H100
    Private Const WM_KEYLAST As Integer = &H108
    Private Const PM_REMOVE As Integer = &H1
    Private Const WM_MOUSEFIRST As Integer = &H200
    Private Const WM_MOUSELAST As Integer = &H20E

    <DllImport("User32.dll", CharSet:=CharSet.Auto)>
    Private Shared Function PeekMessage(ByRef msg As MSG, hwnd As IntPtr, msgMin As Integer, msgMax As Integer, remove As Integer) As Boolean

    End Function

    Private Sub CancelInput()
        Dim msg As New MSG
        While PeekMessage(msg, IntPtr.Zero, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)

        End While
        While PeekMessage(msg, IntPtr.Zero, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)

        End While
    End Sub

End Class

引用返信 編集キー/
■93000 / inTopicNo.4)  Re[1]: キーボードを押した時に二重実行を防止するには
□投稿者/ まりもん (2回)-(2019/11/13(Wed) 10:10:05)
ValueChangedイベントで重い処理を行う設計がよくないのではないでしょうか?
キーボードからの入力で重い処理を行うことを排他したとしても、マウスで操作した場合にValueChangedイベントが連続で発声することになるのではないですか?
引用返信 編集キー/
■93003 / inTopicNo.5)  Re[2]: キーボードを押した時に二重実行を防止するには
□投稿者/ けーえんす (2回)-(2019/11/13(Wed) 11:36:02)
> KOZさん

ありがとうございます。
このコードでうまくいきました。

ちなみに、毎回実行ごとに、キーボードからの処理をキャンセルするわけなので、
連続実行したい場合には、処理速度が落ちませんかね?

CancelInputを
MouseUpに入れれば良いかと思い入れてみたのですが、
MouseUpだとうまくキャンセルすることができませんでした。

引用返信 編集キー/
■93004 / inTopicNo.6)  Re[3]: キーボードを押した時に二重実行を防止するには
□投稿者/ KOZ (30回)-(2019/11/13(Wed) 14:07:17)
No93003 (けーえんす さん) に返信
> ちなみに、毎回実行ごとに、キーボードからの処理をキャンセルするわけなので、
> 連続実行したい場合には、処理速度が落ちませんかね?

キーリピートのみ抑止であれば

    Private _IsKeyDown As New Dictionary(Of Keys, Boolean)

    Private Sub TrackBar1_KeyDown(sender As Object, e As KeyEventArgs) Handles TrackBar1.KeyDown
        Dim state As Boolean
        If Not _IsKeyDown.TryGetValue(e.KeyCode, state) Then
            state = False
        End If
        e.Handled = state
        _IsKeyDown(e.KeyCode) = True
    End Sub

    Private Sub TrackBar1_KeyUp(sender As Object, e As KeyEventArgs) Handles TrackBar1.KeyUp
        _IsKeyDown(e.KeyCode) = False
    End Sub

マウスもドラッグ中は処理せず、ボタンを離したとき実行するなら

    Private _MouseIsDown As Boolean
    Private _MouseDownValue As Integer

    Private Sub TrackBar1_MouseDown(sender As Object, e As MouseEventArgs) Handles TrackBar1.MouseDown
        If e.Button.HasFlag(MouseButtons.Left) Then
            _MouseDownValue = TrackBar1.Value
            _MouseIsDown = True
        End If
    End Sub

    Private Sub TrackBar1_MouseUp(sender As Object, e As MouseEventArgs) Handles TrackBar1.MouseUp
        If e.Button.HasFlag(MouseButtons.Left) Then
            If _MouseDownValue <> TrackBar1.Value Then
                _MouseIsDown = False
                HevyProc(TrackBar1.Value)
            End If
        End If
    End Sub

    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged
        If Not _MouseIsDown Then
            HevyProc(TrackBar1.Value)
        End If
    End Sub

    Private Sub HevyProc(trackBarValue As Integer)
        Debug.Print("重い処理開始 value={0}", trackBarValue)
        Thread.Sleep(1000)
        Debug.Print("重い処理終了")
    End Sub

こんな感じですかね。

引用返信 編集キー/

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


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

このトピックに書きこむ