DOBON.NET DOBON.NETプログラミング掲示板過去ログ

進行状況ダイアログがメインフォームの後ろに隠れてしまう

環境/言語:[OS : Windows XP Professional / 言語 : C# / .NET Framework : 2.0]
分類:[.NET]

【解決したい問題】

進行状況を表示するダイアログを作成しています。
コードはDobonさん作成のものをそのまま利用させて頂いています。

進行状況やキャンセルは問題ありません。
ダイアログフォームはメインウィンドウの中央に表示はされるのですが、沢山のウィンドウを開いていたり、他のソフトを起動していたりすると、ダイアログフォームがメインフォームの後ろに隠れてしまい困っています。

【解決するために何をしたか】

Dobonさんのコードの Show() に

frm.Owner = this.OwnerForm;

というコードを追加してみたりしたのですが、実行時に「他のスレッド〜」というエラーが発生して動作しません。

良い方法があればご教授下さい。
よろしくお願い致します。

開発環境 Visual Studio 2008
エラー メッセージは、ダイアログ ボックスからコピーと貼り付けで全文載せてください。

■No24347に返信(福夫さんの記事)
> 【解決したい問題】
>
> というコードを追加してみたりしたのですが、実行時に「他のスレッド〜」というエラーが発生して動作しません。
失礼しました。

エラーメッセージは

System.InvalidOperationException はハンドルされませんでした。
Message="有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Form1' がアクセスされました。"
Source="System.Windows.Forms"
StackTrace:
場所 System.Windows.Forms.Control.get_Handle()
場所 System.Windows.Forms.Form.UpdateHandleWithOwner()
場所 System.Windows.Forms.Form.set_Owner(Form value)
場所 System.Windows.Forms.Form.AddOwnedForm(Form ownedForm)
場所 alecSystem.Progress.Run() 場所 C:\Alec Project\Visual Studio 2005\Projects\ClassLiblary\alecSystem\alecSystem\classProgress.cs:行 289
場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
場所 System.Threading.ThreadHelper.ThreadStart()
InnerException:

です。
宜しくお願いします。
■No24347に返信(福夫さんの記事)
> 進行状況を表示するダイアログを作成しています。
> コードはDobonさん作成のものをそのまま利用させて頂いています。
どのコードの事を指しているのか、具体的な参照元を引用していただけますか?

> 実行時に「他のスレッド〜」というエラーが発生して動作しません。
進行状況表示を、別のスレッドで行っているのでしょうか。
だとしたら、ワーカースレッドから、フォーム等を直接操作してはいけません。

たとえば BackgroundWorker をお使いなのであれば、メイン処理となる
DoWork イベントの中からは、フォーム等を操作できません。かわりに、
DoWork 内から、ReportProgress メソッドを呼び出して、
メインスレッド側に進捗表示を要請します。その要請を受けると、
ProgressChanged イベントが発生するので、そこで フォーム等を操作します。
http://www.atmarkit.co.jp/fdotnet/dotnettips/436bgworker/bgworker.html

Thread クラスをお使いの場合も同様で、スレッド内からフォームを
直接操作するのではなく、(Begin)Invoke 経由で依頼するようにします。
http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
http://codezine.jp/article/detail/139?p=1
> どのコードの事を指しているのか、具体的な参照元を引用していただけますか?

タイトルは「進行状況ダイアログを表示する」で、コードはここのものをそのまま使用しています(コードが長いのでURLだけで失礼します)。
http://dobon.net/vb/dotnet/programing/progressdialog.html

> 進行状況表示を、別のスレッドで行っているのでしょうか。
> だとしたら、ワーカースレッドから、フォーム等を直接操作してはいけません。

なるほどです。
最初の書込の方法では「ワーカースレッドからフォームを操作する」事になる為に動作しないということですね。
ですがこの問題を回避してダイアログフォームをメインフォームの常に前に表示する方法が分かりません。。

新たに

private void SetOwner()
{
if (form != null && !form.IsDisposed)
form.Owner = this.OwnerForm;
}

と作成し

Show()で
form.Invoke(SetOwner);

などと試してみたのですが、上手く動作しませんでした、、
宜しくお願いします。
これで直るかは分かりませんが、進行状況ダイアログを表示する直前にメインフォームをアクティブにするようにして試していただけますか?
返信が送れてすいません。
何しろ会社でしかVSにさわれないもので、、

> これで直るかは分かりませんが、進行状況ダイアログを表示する直前にメインフォームをアクティブにするようにして試していただけますか?

早速試してみました。
ダイアログを表示した際は手前に表示されるのですが、メインフォームをクリックされたりすると後ろに回ってしまいます。
これはもはや thie.enabled = false; としてメインフォームへの移動を阻止するしか無いのでしょうか、、
できれば this.Owner のような自然な形が良いのですが、、

宜しくお願いします。
ここのTipsで紹介している方法が駄目だということになりますと、順当に処理の部分のみを別スレッドにするというのが良いのではないかと思います。具体的には、普通に進行状況ダイアログを作成して、普通にShowDialogで表示して、進行状況ダイアログが表示されたときにスレッドを作成して、そこで時間のかかる処理を行います。BackgroundWorkerクラスが使えるのであれば、比較的簡単にできるのではないでしょうか。

以下にごく簡単なコードを書いてみました(かなり省略されていることをお許しください)。ここでは、Form2にProgressBarとBackgroundWorkerが貼り付いているとします。かなり改良の余地があるとは思いますが、雰囲気だけでもわかっていただけるでしょうか。

public partial class Form2 : Form
{
private void Form2_Shown(object sender, EventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker)sender;

for (int i = 1; i <= 10; i++)
{
System.Threading.Thread.Sleep(1000);
bw.ReportProgress(i * 10);
}
}

private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
this.Close();
}
}
ありがとうございました。
なんとか解決する事ができました!

管理人さまのコードを参考にしてキャンセルボタンと進捗状況の受け渡しを追加し、上手く動作するものが出来ました。
どうやら難しく考えすぎていたようです、、

仕事の関係上、進捗ダイアログをよく使うので、クラスライブラリとして別ファイルにしたかったのですが、フォーム一つで済みそうなのでこれで行きます。
もう少し研究してクラスライブラ化できるようにしたいと思います。

ありがとうございました!
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板