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

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

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

Re[7]: フォームで処理中に「処理中・・・」画面を表示したい。


(過去ログ 113 を表示中)

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

■66825 / inTopicNo.1)  フォームで処理中に「処理中・・・」画面を表示したい。
  
□投稿者/ taka (1回)-(2013/05/29(Wed) 20:50:48)

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

はじめまして
【VisualStudio 2010】
【Windows7】
【Oracle11g】
以上の環境で現在開発しております。

[MainForm]処理を行うメインフォーム
[SubForm]「処理中・・・」を表示するフォーム

あるフォームでデータ表示や計算処理など時間のかかる処理が発生した場合に
「処理中です・・・」と出るような画面を表示したいです。
ただ表示するだけであれば処理直前に「SubForm.Show」でいいのですが
そのときに、「・」を1秒ごとに「・・」⇒「・・・」⇒「・」というように変化させたり
PictureBoxにはめたGifアニメを動かしたいのですが動きません。

教えていただけませんでしょうか?
よろしくお願いします。

以下は簡単な私が作ったサンプルになります。
誤字脱字あった場合は申し訳ございません。
足りない情報があれば追加させていただきます。
'--------------------------------------------------------
Public Class MainForm

Private Sub Button_Click(Byval sender As System.Object, Byval e As System.EventArgs) Handles Button.Click
Dim frm As New SubForm

frm.Show

'=======時間のかかる処理===========
For i As Integer = 0 To 10
System.Threading.Thread.Sleep(1000)
Next i

frm.Close

End Sub

End Class

'--------------------------------------------------------
Public Class SubForm

Private CallBack As New Threading.TimerCallback(AddressOf Thread_A)
Private P_Flg As Boolean = False

Private Sub Thread_A()
If P_Flg = True Then Exit Sub
P_Flg = True
Me.Invoke(New MethodInvoker(AddresOf ChangeMsg)
Me.Invoke(New MethodInvoker(AddresOf Me.Refresh)
P_Flg = False
End Sub

 Private Sub ChangeMsg()
'メッセージ作成処理
lblMsg.Text = "ここで処理中・・・を表示"
End Sub

Private Sub SubForm_Load(Byval sender As Object, Byval e As System.EventArgs) Handles Me.Load

Dim tim As New System.Threading.Timer(CallBack, Nothing, 0, 1 * 1000)

End Sub

End Class


引用返信 編集キー/
■66827 / inTopicNo.2)  Re[1]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ 魔界の仮面弁士 (230回)-(2013/05/29(Wed) 22:19:26)
No66825 (taka さん) に返信
> PictureBoxにはめたGifアニメを動かしたいのですが動きません。
アニメーション Gif を渡しておけば、勝手に動き出しますよ。

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
Dim f As New Form()

Dim p As New PictureBox()
p.LoadAsync("C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\VS2010ImageLibrary\1041\Animations\Csearch_00.gif")
f.Controls.Add(p)

f.Show(Me)
End Sub


ただし、メインスレッドで重い処理を行っている場合は
再描画されないため、アニメーションされません。


その場合は、重い処理を BackgroundWorker に担当させるなどして
回避してみてください。進捗表示に使うにも便利ですよ。


> そのときに、「・」を1秒ごとに「・・」⇒「・・・」⇒「・」というように変化させたり
1 秒ごとに処理するなら、Timer を使うのが簡単でしょう。

ただしこれもまた、メインスレッドで重い処理を行っていると
そもそも Tick イベントが始まりませんので、ご注意あれ。


> System.Threading.Thread.Sleep(1000)
メインスレッド(画面を持つ UI スレッド)から Sleep を呼ぶことは厳禁です。
引用返信 編集キー/
■66831 / inTopicNo.3)  Re[2]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ taka (2回)-(2013/05/30(Thu) 09:20:09)
魔界の仮面弁士 さん 書き込みありがとうございます!

Gifアニメは処理中でなければ動きますので、

> ただし、メインスレッドで重い処理を行っている場合は
> 再描画されないため、アニメーションされません。

ここにはまっているのかもしれません。


> その場合は、重い処理を BackgroundWorker に担当させるなどして
> 回避してみてください。進捗表示に使うにも便利ですよ。

恥ずかしながらBackgroundWorkerというものを初めて聞きましたので
今から試行錯誤しながら実装してみたいと思います。

>> System.Threading.Thread.Sleep(1000)
> メインスレッド(画面を持つ UI スレッド)から Sleep を呼ぶことは厳禁です。
↑の部分は「アニメーション・文字変換処理を行いたい場合は」ということでよろしいでしょうか?
テスト用に時間のかかる処理をしたく、安易にSleepを使用しておりました。


とりあえずBackgroundWorkerを使用してみて、また結果ご報告させていただきます。
また、その他良い方法ありましたら教えていただけると助かります。

=============================================================
ちなみに「処理」というのは
@Excel出力処理
Aデータベースからのデータ表示処理
B計算処理
というのを想定しており、全ての場合に表示しようと考えております。


引用返信 編集キー/
■66833 / inTopicNo.4)  Re[3]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ 魔界の仮面弁士 (231回)-(2013/05/30(Thu) 11:04:40)
No66831 (taka さん) に返信
> Gifアニメは処理中でなければ動きますので、
>>ただし、メインスレッドで重い処理を行っている場合は
>>再描画されないため、アニメーションされません。
> ここにはまっているのかもしれません。

GIF アニメ表示、あるいは「・・」⇒「・・・」⇒「・」を表示するだけで
他には何もしない EXE を一個作り、それを Process.Start で起動すれば
とりあえず目的は果たせます。

しかし、UI スレッドに重い処理を行わせることは、本来避けるべきです。
「UI スレッドで重い処理を行っているので、その間に別画面を表示する」
と修正するのではなく、
「重い処理を別スレッドに追い出して、UI スレッドは進捗表示を行う」
という修正にすることを検討してみてください。

VB2012 なら、非同期処理も作りやすいのですけれどね。


>>メインスレッド(画面を持つ UI スレッド)から Sleep を呼ぶことは厳禁です。
> ↑の部分は「アニメーション・文字変換処理を行いたい場合は」ということでよろしいでしょうか?

コンソールアプリならば、Sleep しても OK です。しかし WinForm のアプリでは NG です。

スリープさせている間はウィンドウメッセージを処理できなくなりますので、
ウィンドウ(Form/Control など)を持つスレッドは、どのような処理を行っているかに
関わらず、Sleep API や Thread.Sleep メソッドを呼び出してはいけません。
ワーカースレッド内で呼ぶ分には構いませんが。


> 恥ずかしながらBackgroundWorkerというものを初めて聞きましたので
> 今から試行錯誤しながら実装してみたいと思います。

主な流れはこんな感じです。

(1)画面側から、「重い作業を BackgroundWorker に依頼」します。
 具体的には、RunWorkerAsync メソッドを呼び出すということです。

(2)依頼された BackgroundWorker は、DoWork イベントで重い処理を実行します。
 処理結果は、引数 e.Result に代入してください。

(3)DoWork の作業が終わると、RunWorkerCompleted イベントが発生します。
 依頼側(画面)は、引数 e.Result から処理結果を受け取ってください。

また、ProgressChanged イベントを通じて、進捗状況を画面側に報告することも可能です。


DoWork の部分は、別スレッドとして処理が切り離されています。
そのため、この中で重い処理を行っても、画面側には一切影響を与えません。

ただし、依頼者(画面)と作業者(ワーカースレッド)が独立しているということは、
同じオブジェクトを両者で共有することは、基本 NG であることに注意が必要です。

・DoWork 内から、フォームやコントロールを操作することはできません。
・Form や Module のフィールド変数(Private/Public aaa As Integer など)を使うことも NG です。
・ローカル変数は利用できます。DoWork 内で Dim 変数を使うことは問題ありません。

なお、作業開始時に初期パラメータを与えたい場合には、フィールド変数ではなく、
RunWorkerAsync メソッドの引数を通じて渡す必要があります。作業側では、
DoWork イベントの e.Argument からそれを受け取れます。
引用返信 編集キー/
■66841 / inTopicNo.5)  Re[4]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ taka (3回)-(2013/05/30(Thu) 15:33:06)
魔界の仮面弁士 さん ありがとうございます。

> ただし、依頼者(画面)と作業者(ワーカースレッド)が独立しているということは、
> 同じオブジェクトを両者で共有することは、基本 NG であることに注意が必要です。
>
> ・DoWork 内から、フォームやコントロールを操作することはできません。
> ・Form や Module のフィールド変数(Private/Public aaa As Integer など)を使うことも NG です。
> ・ローカル変数は利用できます。DoWork 内で Dim 変数を使うことは問題ありません。

BackgroundWorkerを使用してみたのですが【処理】の中の
Aデータベースからのデータ表示処理(重い処理ではないですが・・・)
で画面コントロールへアクセスする部分で問題にぶち当たりました。
Invokeを使用すればいいのかもしれませんが、処理自体はコーディング済みなため変更は厳しいです・・・

その他いろいろなパタンをためしました。
1.ただ単純にSubFormを表示
(問題)SubFormの再描画が行われずに期待通りの動きをしない。

2.SubForm自体を別スレッドで呼び出し表示させる。
(問題)SubForm表示中にメッセージボックスが出るとメッセージがSubFormで隠れてしまう。
-------また表示させたメッセージボックスで「はい」や「OK」をクリックするとSubFormがMainFormの裏に隠れる。
(問題)別スレッドで動かしているためMainFormをオーナーにできない。

3.SubForm自体は同スレッドで呼び出すが、内部の描画処理を別スレッドに持っていってみる。
(問題)SubFormの再描画が行われずに期待通りの動きをしない。

4.処理をBackgroundWorkerに任せて同スレッドでSubFormを表示
(問題)データ表示処理でMainFormのコントロールへアクセスできない。(Invokeの処理追加は困難)

5.SubFormを別ExeにしてStartProcessで呼び出し(まだ試していません。)
(予想)2と同じ問題が発生しそう・・・予想ですみません。


=========================================================
この発想は画面で処理中の場合に、「フリーズしているわけではなくて処理中なんですよ!」ってアピールしたいとこから始まりました。
Gifアニメなんか見てもらいながらリラックスしてもらおうとかいろいろ考えていった結果煮詰まっております…
重要な機能ではないため、
『そもそも考え方間違ってる!』
『そんなことできるわけない!』
『そんなことしなくても、○○があるよね?』
などの意見があれば、やめるつもりです(><)

なんとか実装したいのですが、難しいものでしょうか?
引用返信 編集キー/
■66842 / inTopicNo.6)  Re[5]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ 魔界の仮面弁士 (232回)-(2013/05/30(Thu) 17:50:55)
No66841 (taka さん) に返信
> で画面コントロールへアクセスする部分で問題にぶち当たりました。
ReportProgress の引数に途中経過の情報を渡し、画面側が
ProgressChanged イベントでそれを受けて、画面に表示することはできます。


> Invokeを使用すればいいのかもしれませんが、処理自体はコーディング済みなため変更は厳しいです・・・
どのようなコードになっているのか分からないので、厳しいかどうかは判断できませんが、
非同期化を意識した設計になっていない場合、修正は痛みを伴うかも知れませんね。
基本的にはコントロールやフォームを直接操作しないように組みなおす必要がありますので。


「コントロールを読み書きする」のは UI スレッドの役目、
「データを処理する」のは UI もしくはワーカースレッドの役目です。

たとえば、データ処理とコントロール制御が充分に分離されているなら、

Sub Button1_Click(…
 Dim 初期パラメータ As String = TextBox1.Text
 Dim 処理結果 As String = DB書き込み処理(初期パラメータ) '★
 ListBox1.Items.Add(処理結果)
End Sub

という同期処理を、BackgroundWorker での非同期処理に書き換えて

Sub Button1_Click(…
 Button1.Enabled = False
 Dim 初期パラメータ As String = TextBox1.Text
 BackgrondWorker1.RunWorkerAsync(初期パラメータ)
End Sub
Sub BackgrondWorker1_DoWork(…
 e.Result = DB書き込み処理(CStr(e.Argument)) '★
End Sub
Sub BackgrondWorker1_RunWorkerCompleted(…
 Dim 処理結果 As String = CStr(e.Result)
 ListBox1.Items.Add(処理結果)
 Button1.Enabled = True
End Sub

などとできます。この程度であれば、話は比較的単純であろうかと思います。
 
あるいは、作業部分を 別 EXE にして、
 p = Process.Start("DB書込.EXE", TextBox1.Text)
とする手もありますね。(処理結果はファイル等で受け取る)


しかし上記の ★DB書き込み処理 メソッドの内部において、
引数で渡されたデータだけではなく、コントロールの操作までも
行ってしまっているのであれば、途端にややこしいことになります。

その場合は、
・Invoke で、画面操作を UI スレッドに依頼する
・コントロールに依存しないよう書き換える
などの改修が発生することになるでしょう。



> Gifアニメなんか見てもらいながらリラックスしてもらおうとかいろいろ考えていった結果煮詰まっております…
「煮詰まる」は本来、結論に近づく/考えがまとまる、といったポジティブな意味ですね。

# 最近は、行き詰まった様子も含めたネガティブな意味でも使われるようですが、
# それに違和感を持ってしまうのは……若くないという証拠でしょうか。(汗)


> (問題)別スレッドで動かしているためMainFormをオーナーにできない。
この条件が必要なら、時間のかかる作業を UI スレッドに行わせないよう
処理を見直す必要があります。

ただでさえ重い処理を行っているときに、他の画面処理を行う余裕はありません。
選択肢としては、
 (1) 処理を見直して作業時間を短縮する。
 (2) 一度にまとめて処理させず、分割して少しずつ処理していく。
 (3) 時間のかかる処理を、他の誰か(別プロセスや別スレッドなど)に依頼する。
といったところでしょうか。2 と 3 を合わせて、並行同時実行というパターンもありますが。


> 3.SubForm自体は同スレッドで呼び出すが、内部の描画処理を別スレッドに持っていってみる。
描画処理というのが何を意味するのか分かりませんが、ワーカースレッドが
Bitmap を作って描画していき、それを ProgressChanged や RunWorkerCompleted で
PictureBox 等に表示する…という流れにはできます。

あるいは Invoke という手もありますが、いずれにせよ、その描画結果を
画面に反映させる作業は、最終的には UI スレッドが担当することになります。



>> VB2012 なら、非同期処理も作りやすいのですけれどね。
ちなみに、先の Sleep コードを 2012 構文で置き換えるとこんな感じ。

Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
 Button1.Enabled = False

 For i As Integer = 0 To 10
  Await Task.Delay(1000)
 Next i

 Button1.Enabled = True
End Sub
引用返信 編集キー/
■66845 / inTopicNo.7)  Re[5]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ 魔界の仮面弁士 (233回)-(2013/05/30(Thu) 22:08:15)
No66841 (taka さん) に返信
> 2.SubForm自体を別スレッドで呼び出し表示させる。
この場合、SubForm を閉じるための指示方法も検討する必要がありそうですね。


> (問題)SubForm表示中にメッセージボックスが出るとメッセージがSubFormで隠れてしまう。
これは、メインスレッドからメッセージボックスを表示した場合でしょうか。

MsgBox で、第2引数に MsgBoxStyle.MsgBoxSetForeground を指定するか、もしくは
MessageBox.Show で、options:=CType(vbMsgBoxSetForeground, MessageBoxOptions) を
指定すると、隠れずに表示されます。

ただし、メインフォームとサブフォームの親子関係を結べるわけでは無いため、
メッセージボックスが表示された瞬間、呼び出し元スレッドのフォームが
サブフォームより手前に来てしまい、解決策とはなりませんが。


> -------また表示させたメッセージボックスで「はい」や「OK」をクリックするとSubFormがMainFormの裏に隠れる。
> (問題)別スレッドで動かしているためMainFormをオーナーにできない。

表示されたときにも、フォームの表示順を崩したくないのであれば、
親子関係のかわりに TopMost で代用する逃げ方はあります。

TopMost 同士なら、後から表示されたほうが手前に来るという性質を利用して、
メッセージボックスの代わりに、自作のモーダル フォームを使って
 メインフォーム.TopMost = False
 サブフォーム.TopMost = True
 自作メッセージボックス.TopMost = True
とするパターンです。お奨めはできませんけれども。


> なんとか実装したいのですが、難しいものでしょうか?
「UI スレッドに重い処理をやらせてはいけない」ということで、
時間のかかる処理をワーカースレッドに追いやるのが正攻法かと。

この場合、ワーカースレッドが画面部品に依存しているのであれば
まずはその依存関係を見直す必要がありますが、それが
難しい作業となるのかどうかは、こちらでは判断できません。
引用返信 編集キー/
■66877 / inTopicNo.8)  Re[6]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ taka (4回)-(2013/06/01(Sat) 11:44:13)
Imports System.Windows.Forms
Public Class ProcInfoDialog

#Region "■変数            "
    Private P_strMsgString As String = ""
    Private P_strChangeString As String = "・"
    Private P_intCnt As Integer = 0
    Private P_image As System.Drawing.Image = Nothing
    Private P_MsgFont As System.Drawing.Font = New System.Drawing.Font("MS UI Gothic", 12)

    '処理中ダイアログを表示するスレッド
    Private thread As System.Threading.Thread = Nothing
    'SyncLock用のオブジェクト

    Private ReadOnly syncObject As New Object()
    '処理中ダイアログが表示されるまで待機するための待機ハンドル
    Private splashShownEvent As System.Threading.ManualResetEvent = Nothing
#End Region

#Region "■プロパティ         "

#Region "◇ChangeString                "
    <System.ComponentModel.Description("処理中に変わり続ける文字列を指定します。")> _
   <System.ComponentModel.Category("メッセージ")> _
    Public Property ChangeString As String
        Get
            Return P_strChangeString
        End Get
        Set(ByVal value As String)
            P_strChangeString = value
        End Set
    End Property
#End Region

#Region "◇MsgString                   "
    <System.ComponentModel.Description("処理中表示されるメッセージを指定します。")> _
    <System.ComponentModel.Category("メッセージ")> _
    Public Property MsgString As String
        Get
            Return P_strMsgString
        End Get
        Set(ByVal value As String)
            P_strMsgString = value
        End Set
    End Property
#End Region

#Region "◇Disp                        "
    <System.ComponentModel.Description("処理中表示されるイメージを指定します。")> _
    <System.ComponentModel.Category("イメージ")> _
    Public Property DispImage As System.Drawing.Image
        Get
            Return P_image
        End Get
        Set(ByVal value As System.Drawing.Image)
            P_image = value
        End Set
    End Property
#End Region

#Region "◇MsgFont                     "
    <System.ComponentModel.Description("処理中表示される文字のフォントを指定します。")> _
    <System.ComponentModel.Category("メッセージ")> _
    Public Property MsgFont As System.Drawing.Font
        Get
            Return P_MsgFont
        End Get
        Set(ByVal value As System.Drawing.Font)
            P_MsgFont = value
        End Set
    End Property
#End Region

#End Region

#Region "■処理中ダイアログ表示処理  "
    ''' <summary>処理中ダイアログ表示処理 </summary>
    ''' <param name="mainForm">メインフォーム</param>
    Public Sub ShowMsgDialog(ByVal mainForm As Form)
        SyncLock syncObject
            If (Not thread Is Nothing) Then
                Return
            End If

            '待機ハンドルの作成
            splashShownEvent = New System.Threading.ManualResetEvent(False)

            'スレッドの作成
            thread = New System.Threading.Thread( _
                New System.Threading.ThreadStart(AddressOf StartThread))
            thread.Name = "ProcInfoDialog"
            thread.IsBackground = True
            thread.SetApartmentState(System.Threading.ApartmentState.STA)
            '.NET Framework 1.1以前では、以下のようにする
            'thread.ApartmentState = System.Threading.ApartmentState.STA
            'スレッドの開始

            thread.Start()
        End SyncLock
    End Sub

    ''' <summary>Splash処理中ダイアログ表示処理表示する</summary>
    Public Sub ShowMsgDialog()
        ShowMsgDialog(Nothing)
    End Sub
#End Region

#Region "■処理中ダイアログ表示処理  "
    ''' <summary>処理中ダイアログを消す</summary>
    Public Sub CloseMsgDialog()
        SyncLock syncObject
            If thread Is Nothing Then
                Return
            End If

            '処理中ダイアログが表示されるまで待機する

            If Not splashShownEvent Is Nothing Then
                splashShownEvent.WaitOne()
                splashShownEvent.Close()
                splashShownEvent = Nothing
            End If

            '処理中ダイアログを閉じる
            'Invokeが必要か調べる

            If Me.InvokeRequired Then
                Me.Invoke(New MethodInvoker(AddressOf CloseProcInfoDialog))
            Else
                CloseProcInfoDialog()
            End If

            thread = Nothing
        End SyncLock
    End Sub
#End Region

#Region "■スレッドで開始するメソッド "
    'スレッドで開始するメソッド'
    Private Sub StartThread()
        '処理中ダイアログが表示されるまでCloseMsgDialogメソッドをブロックする'
        AddHandler Me.Activated, AddressOf _form_Activated
        '処理中ダイアログを表示する'
        Application.Run(Me)
    End Sub

#End Region

#Region "■クローズメソッド      "
    '処理中ダイアログのCloseメソッドを呼び出す'
    Private Sub CloseProcInfoDialog()
        Me.Close()
    End Sub
#End Region

#Region "■ダイアログが表示されたとき "
    '処理中ダイアログが表示された時'
    Private Sub _form_Activated(ByVal sender As Object, _
                                       ByVal e As EventArgs)
        RemoveHandler Me.Activated, AddressOf _form_Activated
        'CloseMsgDialogメソッドの待機を解除'
        If Not splashShownEvent Is Nothing Then
            splashShownEvent.Set()
        End If
    End Sub
#End Region

#Region "■オブジェクトイベント    "

#Region "◆フォーム          "

#Region "○Load                        "

    Private Sub ProcInfoDialog_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'メッセージの表示'
        lblMsg.Text = P_strMsgString

        '変化する文字列が指定されている場合はタイマーを起動します。'
        If P_strChangeString <> "" Then
            timMsg.Enabled = True
            timMsg.Interval = 1000
        End If

        'イメージが設定されていない場合はフォームサイズを小さくします。(ピクチャーボックスを隠す)'
        If P_image Is Nothing Then
            Me.Width = 197
        Else
            picImg.Image = P_image
            Me.Width = 252
        End If

        'フォントの設定

        lblMsg.Font = P_MsgFont
    End Sub
#End Region

#End Region

#Region "◆タイマー          "

#Region "○Tick                        "
    Private Sub timMsg_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles timMsg.Tick
        If Me.P_strChangeString <> "" Then
            If P_intCnt = 3 Then P_intCnt = -1
            P_intCnt += 1
            lblMsg.Text = P_strMsgString.PadRight(Len(P_strMsgString) + P_intCnt, CChar(P_strChangeString))
        End If
    End Sub
#End Region

#End Region

#End Region

End Class
==================================================================================

引用返信 編集キー/
■66878 / inTopicNo.9)  Re[7]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ taka (5回)-(2013/06/01(Sat) 11:44:43)
2013/06/01(Sat) 11:54:47 編集(投稿者)
Public Class ProcInfo

#Region "■変数            "
    Private P_strMsgString As String = ""
    Private P_strChangeString As String = "・"
    Private P_image As System.Drawing.Image = Nothing
    Private P_MsgFont As System.Drawing.Font = New System.Drawing.Font("MS UI Gothic", 12)
    Private P_ProcInfoMsg As ProcInfoDialog = Nothing
    Private P_BackColor As System.Drawing.Color = Drawing.Color.Aquamarine
#End Region

#Region "■プロパティ         "

#Region "◇ChangeString                "
    <System.ComponentModel.Description("処理中に変わり続ける文字列を指定します。")> _
   <System.ComponentModel.Category("メッセージ")> _
    Public Property ChangeString As String
        Get
            Return P_strChangeString
        End Get
        Set(ByVal value As String)
            P_strChangeString = value
        End Set
    End Property
#End Region

#Region "◇MsgString                   "
    <System.ComponentModel.Description("処理中表示されるメッセージを指定します。")> _
    <System.ComponentModel.Category("メッセージ")> _
    Public Property MsgString As String
        Get
            Return P_strMsgString
        End Get
        Set(ByVal value As String)
            P_strMsgString = value
        End Set
    End Property
#End Region

#Region "◇Disp                        "
    <System.ComponentModel.Description("処理中表示されるイメージを指定します。")> _
    <System.ComponentModel.Category("イメージ")> _
    Public Property DispImage As System.Drawing.Image
        Get
            Return P_image
        End Get
        Set(ByVal value As System.Drawing.Image)
            P_image = value
        End Set
    End Property
#End Region

#Region "◇MsgFont                     "
    <System.ComponentModel.Description("処理中表示される文字のフォントを指定します。")> _
    <System.ComponentModel.Category("メッセージ")> _
    Public Property MsgFont As System.Drawing.Font
        Get
            Return P_MsgFont
        End Get
        Set(ByVal value As System.Drawing.Font)
            P_MsgFont = value
        End Set
    End Property
#End Region

#Region "◇BackColor                   "
    Public Property BackColor As System.Drawing.Color
        Get
            Return P_BackColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            P_BackColor = value
        End Set
    End Property
#End Region

#End Region

#Region "■処理中ダイアログ表示処理  "
    ''' <summary>処理中ダイアログ表示処理 </summary>
    Public Sub ShowMsgDialog()
        P_ProcInfoMsg = New ProcInfoDialog
        With P_ProcInfoMsg
            .MsgString = Me.MsgString
            .ChangeString = Me.ChangeString
            .BackColor = Me.BackColor
            .DispImage = Me.DispImage
        End With

        P_ProcInfoMsg.ShowMsgDialog()

    End Sub

#End Region

#Region "■処理中ダイアログ表示処理  "
    ''' <summary>処理中ダイアログを消す</summary>
    Public Sub CloseMsgDialog()
        P_ProcInfoMsg.CloseMsgDialog()
        P_ProcInfoMsg.Dispose()
    End Sub
#End Region

End Class

==================================
'呼出画面での処理
 Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button.Click

   
        
        Dim pd As New ProcInfo
        With pd
            .BackColor = Color.Aquamarine
            .MsgFont = New System.Drawing.Font("MS UI Gothic", 12, FontStyle.Italic)
            If Dir("C:\Icon_Graphic\image.gif") <> "" Then
                .DispImage = System.Drawing.Image.FromFile("C:\Icon_Graphic\image.gif")
            End If
        End With

        pd.ShowMsgDialog()

        MsgBox("エラーメッセージなど")  

        pd.CloseMsgDialog()

    End Sub

引用返信 編集キー/
■66879 / inTopicNo.10)  Re[6]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ taka (6回)-(2013/06/01(Sat) 11:53:59)
魔界の仮面弁士 さん いつもありがとうございます。

障害対応に追われており、なかなか試せませんでした><
すみません。

上に現在試しているソースコードそのまま貼り付けてみました。
現在は2.SubForm自体を別スレッドで呼び出し行っております。
このパタンで動作させると、「初期起動時」?のみ(それも端末による・・・)
「ウィンドウハンドルが作成される前、コントロールでinvokeまたはbegininvokeを呼び出せません」
というエラーが表示されます。
これは現在調査中です。

使い方としてはProcInfoをPublicで宣言し、各画面でShowMsgDialogを呼び出しするイメージです。

また時間のかかる処理といいながらも、UIが行えない(待ち)タイミングで表示する予定で
1秒で終了してしまうこともあります。
処理にはデータ表示を含んでおり、画面部品に依存しております。
本来はこのタイミングで表示するべきでないのかもしれません。


引用返信 編集キー/
■66881 / inTopicNo.11)  Re[7]: フォームで処理中に「処理中・・・」画面を表示したい。
□投稿者/ 魔界の仮面弁士 (236回)-(2013/06/01(Sat) 16:43:10)
No66879 (taka さん) に返信
> 上に現在試しているソースコードそのまま貼り付けてみました。
投稿された Class は、2 つとも Form なのですよね?


> 「ウィンドウハンドルが作成される前、コントロールでinvokeまたはbegininvokeを呼び出せません」
恐らくは、IsHandleCreated が False の状態だったのかと。


> 'スレッドで開始するメソッド'
> Private Sub StartThread()
>   '処理中ダイアログが表示されるまでCloseMsgDialogメソッドをブロックする'
>   AddHandler Me.Activated, AddressOf _form_Activated
>   '処理中ダイアログを表示する'
>   Application.Run(Me)
> End Sub

上記 Me のインスタンスは、メインスレッドで生成され、
ProcInfo.P_ProcInfoMsg フィールドで管理されていますので、
それをワーカー側で扱うのは望ましくありません。
ワーカー側で表示されるフォームは、ワーカー側で管理すべきかと。



> '呼出画面での処理
やりたいことは
 pd = New 進捗管理クラス()
 pd.BackColor = 進捗フォームの背景色
 pd.ShowMsgDialog()  '進捗フォームを別スレッドで非同期表示
 ' :
 'メインスレッド側の処理
 ' :
 pd.ClosMsgDialog()  '進捗フォームの終了要求
で宜しいでしょうか?

だとすればこの場合、「進捗管理クラス」を Form にするのではなく、
「進捗管理クラスが、内部で Private な Form を表示させる」だけにします。



単純な実装としてはこんな感じ。

Imports System.Drawing
Imports System.ComponentModel

Public Class Form1
    Inherits Form

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        '実際には匿名型ではなく、明示的な型を用意した方が良いでしょう
        Dim 設定値 = New With {
            .BackColor = Color.Cyan,
           .ImageFile = "C:\Icon_Graphic\image.gif",
            .MsgFont = New Font("MS UI GothicD", 12, FontStyle.Italic)
        }

        '下記では、テストしやすいよう BackgroundWorker を使っていますが
        '実際には再利用しやすいように 進捗管理クラス を別途用意しておき、
        'そのクラス内でワーカースレッドを作成するようにしてみてください
        Me.BackgroundWorker1.WorkerSupportsCancellation = True
        Me.BackgroundWorker1.RunWorkerAsync(設定値)

        MsgBox("エラーメッセージなど")

        '終了依頼
        BackgroundWorker1.CancelAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim 設定値 = e.Argument
        Dim bgw = DirectCast(sender, BackgroundWorker)

        Dim f As New Form2()
        f.BackColor = 設定値.BackColor
        f.Label1.Font = 設定値.MsgFont
        f.PictureBox1.LoadAsync(設定値.ImageFile)
        f.Timer1.Interval = 72
        f.Timer1.Start()
        AddHandler f.Timer1.Tick, Sub() If bgw.CancellationPending Then f.Close()
        f.Timer1.Start()
        Application.Run(f)
    End Sub

    '外部から直接呼ばれないよう、Private Class にした方が安全かも
    'Private Class Form2
    '    Inherits Form
    '    '    :
    'End Sub
End Class


ただし、これはあまり好ましい実装ではありません。
メイン画面で時間のかかる処理が行われれば、アプリケーションが
暫く応答なし状態になる点は変わりませんし、先の投稿で問題視されていた
進捗画面とメイン画面の ZOrder 順に関する問題を抱える結果にもなるからです。

先にも少し書きましたが、UI の扱いはメインスレッドに任せておき、
時間のかかる作業の方をワーカースレッドに任せるのが、本来の対処方法です。


> 処理にはデータ表示を含んでおり、画面部品に依存しております。
データ表示だけ、相手に依頼してあげてください。

ワーカースレッドが、自身でデータを表示するのではなく、
ワーカースレッドが、UI スレッドにデータを BeginInvoke で渡して、
UI スレッド自身に表示させる形をとります。
(InvokeRequired プロパティの解説なども参考に)


> 1秒で終了してしまうこともあります。
ちなみに Windows 8 のストアアプリでは、
わずかでも時間のかかる可能性のある処理(50ミリ秒以上が目安)は、
全て非同期処理にすることが求められています。

引用返信 編集キー/


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

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -