- PR -

イベント内でのメッセージボックス使用による再入について

投稿者投稿内容
hagi
会議室デビュー日: 2006/04/07
投稿数: 6
投稿日時: 2006-04-07 15:28
WinXP_SP1+VB.NET2003でデスクトップアプリケーションを開発しています。

メインフォームでSystem.Windows.Forms.DateTimePickerを使用しています。
DateTimePicker.ValueChangedイベントで処理を行い、処理内容によって
MessageBoxを表示させ、分岐させています。このMessageBox表示時の
イベント再入で無限ループに入ってしまい、困っています。
月を変更する"矢印"を押すと"矢印を押した"というイベント?
が完了する前にMessageBoxを表示してしまうせいか、
ValueChangedイベント処理関数に何回も処理が入ってしまいます。
(DateTimePickerは矢印を押した方向に月更新が連続で進んでいます。)

[コード例]
Public DatePicker as DateTimePicker
Private Sub DatePicker_ValueChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles DatePicker.ValueChanged
'何かの処理
if MessageBox.Show("Test", "Test", MessageBoxButtons.YesNoCancel) _
= MsgBoxResult.Yes then
'分岐の処理
end if
End Sub
'コード5行目のMessageBox処理から別のイベントが2行目に再入してきます。

内容的にはこのスレッドと近いです。
[VB.NET タイマーイベントにて]
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=29364&forum=7&start=8

私自身もWindows.Form.Timerで同じ経験を過去にして、その時はTimer使用を
辞めました(Threadに置き換えたと思います)。今回は逃げ方に困っています。
MessageBoxを辞めれば良いのですが、イベント処理内でMessageBoxを
使用できないのは些か残念です。
上記のような状況で、再入を防ぐ良いアイデアを何方か御存知でしたら
教えて頂けると助かります。長文失礼致しました。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-07 16:32
以下のように実装しましたが、再現しませんでした。

コード:

    Private Sub DateTimePicker1_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.ValueChanged
        If MessageBox.Show("Test", "Test", MessageBoxButtons.YesNoCancel) = DialogResult.Yes Then
            '/ 特に何もなし
        End If
    End Sub


「別のイベント」とは何でしょうか?

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
hagi
会議室デビュー日: 2006/04/07
投稿数: 6
投稿日時: 2006-04-07 16:50
返信有難うございます。別のイベントとは、上手く表現できないのですが、
MessageBoxを表示したイベントとは別に、メインフォームに渡されたイベント、という意味です。日にち選択では発生しないのですが、月表示をスクロール
させると発生してしまいます。以下のようなコードでスタックトレースを
見てみました。

[コード例]
Dim i As Integer
Private Sub DateTimePicker1_ValueChanged(略)
Try
i += 1
If i = 1 or i = 3 Then '場合によってコメントアウトして下さい。
Throw New Exception("Error " & i & " kaime")
End If
Trace.WriteLine("Count=" & i)
MsgBox("test" & i)
Catch ex As Exception
Trace.Fail(ex.Message)
End Try
End Sub
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-07 17:02
引用:

hagiさんの書き込み (2006-04-07 16:50) より:

日にち選択では発生しないのですが、月表示をスクロールさせると発生してしまいます。


日にち "選択" と月を "スクロール" では比較するに値しないような気がするので良くわかりませんが、

引用:

MessageBoxを表示したイベントとは別に、メインフォームに渡されたイベント、という意味です。


うまく説明できないのであれば、ミニマム コードを書いて頂けませんか?
イベントに目星が付いているならば、コメント アウトなどしてミニマム テストもされていると思いますし。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
hagi
会議室デビュー日: 2006/04/07
投稿数: 6
投稿日時: 2006-04-07 17:14
すみません、tabを空白に置き換えなかったので左によってしまいました・・・

処理がループしている時のスタックトレースの結果です。
非常に長く、判り辛くて申し訳ありません。

at Form1.DateTimePicker1_ValueChanged(〜略〜) D:\〜略〜
                      ↑ 2回目のForm内関数の呼出です。
at DateTimePicker.OnValueChanged(EventArgs eventargs)
at DateTimePicker.WmDateTimeChange(Message& m)
at DateTimePicker.WmReflectCommand(Message& m)
at DateTimePicker.WndProc(Message& m)
at ControlNativeWindow.OnMessage(Message& m)
at ControlNativeWindow.WndProc(Message& m)
at NativeWindow.DebuggableCallback(IntPtr hWnd, _
Int32 msg, IntPtr wparam, IntPtr lparam)
at UnsafeNativeMethods.SendMessage(HandleRef hWnd, _
Int32 msg, IntPtr wParam, IntPtr lParam)
at Control.SendMessage(Int32 msg, IntPtr wparam, IntPtr lparam)
at Control.ReflectMessageInternal(IntPtr hWnd, Message& m)
at Control.WmNotify(Message& m)
at Control.WndProc(Message& m)
at ScrollableControl.WndProc(Message& m)
at ContainerControl.WndProc(Message& m)
at Form.WndProc(Message& m)
at ControlNativeWindow.OnMessage(Message& m)
at ControlNativeWindow.WndProc(Message& m)
at NativeWindow.DebuggableCallback(IntPtr hWnd, _
Int32 msg, IntPtr wparam, IntPtr lparam)
at UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, _
IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at NativeWindow.DefWndProc(Message& m)
at Control.DefWndProc(Message& m)
at Control.WmNotify(Message& m)
at Control.WndProc(Message& m)
at DateTimePicker.WndProc(Message& m)
at ControlNativeWindow.OnMessage(Message& m)
at ControlNativeWindow.WndProc(Message& m)
at NativeWindow.DebuggableCallback(IntPtr hWnd, _
Int32 msg, IntPtr wparam, IntPtr lparam)
         ↑ 再度、イベントをキャッチしてます。
at SafeNativeMethods.MessageBox(HandleRef hWnd, _
String text, String caption, Int32 type)
at MessageBox.ShowCore(〜略〜)
at MessageBox.Show(〜略〜)           ← MessageBoxの表示です
at Interaction.MsgBox(Object Prompt, MsgBoxStyle Buttons, Object Title)
at Form1.DateTimePicker1_ValueChanged(〜略〜) D:\〜略〜
                      ↑ 1回目のForm内関数の呼出です。
at DateTimePicker.OnValueChanged(EventArgs eventargs)
at DateTimePicker.WmDateTimeChange(Message& m)
at DateTimePicker.WmReflectCommand(Message& m)
at DateTimePicker.WndProc(Message& m)
at ControlNativeWindow.OnMessage(Message& m)
at ControlNativeWindow.WndProc(Message& m)
at NativeWindow.DebuggableCallback(IntPtr hWnd, _
Int32 msg, IntPtr wparam, IntPtr lparam)
at UnsafeNativeMethods.SendMessage(HandleRef hWnd, _
Int32 msg, IntPtr wParam, IntPtr lParam)
at Control.SendMessage(Int32 msg, IntPtr wparam, IntPtr lparam)
at Control.ReflectMessageInternal(IntPtr hWnd, Message& m)
at Control.WmNotify(Message& m)
at Control.WndProc(Message& m)
at ScrollableControl.WndProc(Message& m)
at ContainerControl.WndProc(Message& m)
at Form.WndProc(Message& m)
at ControlNativeWindow.OnMessage(Message& m)
at ControlNativeWindow.WndProc(Message& m)
at NativeWindow.DebuggableCallback(IntPtr hWnd, _
Int32 msg, IntPtr wparam, IntPtr lparam) ← ScrollBarを押してます。
at UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, _
IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at NativeWindow.DefWndProc(Message& m)
〜略〜 この上から1つの操作(マウスダウン)で処理が連続しています。
at ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at Application.Run(Form mainForm)
at Form1.Main() D:\〜略〜

ミニマムコード・・・了解です。コード自体は先述のコード例でほぼ全てなんですが、
サンプルを起こしてソース全文を上手く載せられるようにします。
hagi
会議室デビュー日: 2006/04/07
投稿数: 6
投稿日時: 2006-04-07 17:26
以下、サンプルコード全文です。MonthCalendarでも発生するので変更しました。

Public Class Form1
Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend WithEvents MonthCalendar1 As System.Windows.Forms.MonthCalendar
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.MonthCalendar1 = New System.Windows.Forms.MonthCalendar
Me.SuspendLayout()
'
'MonthCalendar1
'
Me.MonthCalendar1.Location = New System.Drawing.Point(64, 40)
Me.MonthCalendar1.Name = "MonthCalendar1"
Me.MonthCalendar1.TabIndex = 0
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Controls.Add(Me.MonthCalendar1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region

Public count As Integer
Private Sub MonthCalendar1_DateChanged _
(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DateRangeEventArgs) _
Handles MonthCalendar1.DateChanged
Try
count += 1
Trace.WriteLine("Count=" & count)
MessageBox.Show("Count=" & count, "Test", MessageBoxButtons.OK)
Catch ex As Exception
Trace.Fail(ex.Message)
End Try
End Sub
End Class

色々な操作をすると、そのうちループに入って例外エラーが発生します。
私が大きな勘違いをしているかもしれません。御指摘頂けると助かります。
既成のカレンダーを使用するのを辞めようかとも考えています。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-04-07 17:51
なるほど、再現しました。

どうやらフラグを設けても月が遷移するのが止まらないということは、
MessageBox が表示されたタイミングで、[月移動] ボタンを離したことにされてないようです。

コード:

    Option Strict On

    Public Class Form2
        Inherits System.Windows.Forms.Form

    #Region " Windows フォーム デザイナで生成されたコード "

        Public Sub New()
            MyBase.New()
            InitializeComponent()
        End Sub

        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (Me.components Is Nothing) Then
                    Me.components.Dispose()
                End If
            End If

            MyBase.Dispose(disposing) 
        End Sub 

        Private components As System.ComponentModel.IContainer 
        Private WithEvents MonthCalendar1 As System.Windows.Forms.MonthCalendar 

        <System.Diagnostics.DebuggerStepThrough()> _
        Private Sub InitializeComponent()
            Me.MonthCalendar1 = New System.Windows.Forms.MonthCalendar
            Me.SuspendLayout()
            '
            'MonthCalendar1
            '
            Me.MonthCalendar1.Location = New System.Drawing.Point(64, 40)
            Me.MonthCalendar1.Name = "MonthCalendar1"
            Me.MonthCalendar1.TabIndex = 0
            '
            'Form1
            '
            Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
            Me.ClientSize = New System.Drawing.Size(292, 273)
            Me.Controls.Add(Me.MonthCalendar1)
            Me.Name = "Form1"
            Me.Text = "Form1"
            Me.ResumeLayout(False)
        End Sub

    #End Region

        Private count        As Integer
        Private dateChanging As Boolean

        Private Sub MonthCalendar1_DateChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DateRangeEventArgs) Handles MonthCalendar1.DateChanged
            If Me.dateChanging Then
                Return
            End If

            Try
                Me.count += 1
                Me.dateChanging = True
                MessageBox.Show("Count=" & count, "Test", MessageBoxButtons.OK)
            Catch ex As Exception
                System.Diagnostics.Trace.Fail(ex.Message)
            Finally
                Me.dateChanging = False
            End Try
        End Sub

    End Class


MessageBox の代わりに Form を表示しても再現するんですね。
MessageBox を使うのをやめるしかなさそうですね。
ErrorProvider とか別のものを使った方が良さそうですね。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-04-07 21:18
まずは設計の見直し、ですかね。

 ValueChanged イベントは、ユーザが値を更新している最中に発生します。分岐処理が何をしているかわからないので、例えば、

他のところにある“前半・後半フラグ”によって、設定できる月を 1〜6 月、7〜12 月に制限する。
制限に違反する入力がある場合、警告メッセージを出す。

としましょう。すると、フラグが“後半”を意味しているとき、12 月を表すために "1" を入力したところで、制限違反を示す警告が現れることになります。
 他に、10 月を 11 月に修正しようとして、"0" を削除しても、制限違反になります。

 これでは、明らかにユーザビリティが悪いです。

 他のイベントに処理を移すことが出来ないか、考えましょう。

〆 written by Jitta@わんくま同盟 on 2006/04/07
□ Microsoft MVP for Visual Developer ASP/ASP.NET October, 2005 - September, 2006

スキルアップ/キャリアアップ(JOB@IT)