2013-08-15 23 views
8

この質問に何回も答えても謝りますが、私にとってはうまくいく答えが見つからないようです。私は、アプリケーションが長時間実行されているタスクを実行している間、さまざまな進行状況メッセージを表示するモーダルウィンドウを作成したいと考えています。これらのタスクは別のスレッドで実行され、プロセスのさまざまな段階で進行状況ウィンドウのテキストを更新できます。クロススレッド通信はすべてうまく動作しています。問題は、ウィンドウを他のアプリケーションウィンドウ(コンピュータのすべてのアプリケーションではなく)の上に置くことができず、上に留まり、親ウィンドウとのやりとりを防ぎ、作業を続けることができないということです。ここでWPFモーダル進捗ウィンドウ

は、私がこれまで試したものです:

はまず、私のスプラッシュウィンドウはウィンドウクラスを拡張し、メッセージボックスを更新するためのメソッドを持つカスタムクラスです。私はスプラッシュクラスの新しいインスタンスを早期に作成し、必要に応じて表示/非表示にします。例最も単純で

、私は窓をインスタンス化し、その上に.Show()を呼び出す:これは正しくウィンドウが表示され、初期化タスクを処理するための私のコードを実行し続け

//from inside my secondary thread 
this._splash.Dispatcher.Invoke(new Action(() => this._splash.Show()); 

//Do things 
//update splash text 
//Do more things 

//close the splash when done 
this._splash.Dispatcher.Invoke(new Action(() => this._splash.Hide()); 

を、それは私が上でクリックすることができます親ウィンドウを前面に持ってきてください。

次は私がメインウィンドウを無効にし、後で再有効化しようとした:

Application.Current.Dispatcher.Invoke(new Action(() => this.MainWindow.IsEnabled = false)); 

//show splash, do things, etc 

Application.Current.Dispatcher.Invoke(new Action(() => this.MainWindow.IsEnabled = true)); 

これは、ウィンドウ内のすべての要素を無効にしますが、私はまだメインウィンドウをクリックして、スプラッシュスクリーンの前でそれを持って来ることができますそれは私が望むものではありません。

次へスプラッシュウィンドウの一番上のプロパティを使用してみました。これはすべての前にそれを保持し、メインウィンドウのIsEnabledプロパティを設定することと関連して、私は対話を防ぐことができますが、スプラッシュ画面が他のアプリケーションを含めてすべての前面に表示されます。私はそれも望んでいない。私はこのアプリケーションの中で一番上のウィンドウにしたいだけです。

次に、.Show()の代わりに.ShowDialog()を使用することについての投稿が見つかりました。私はこれを試して、それは正しくダイアログを示して、私は親ウィンドウをクリックすることを許可しませんでしたが、.ShowDialog()を呼び出すと、コードを実行し続ける前にダイアログを閉じるのを待っているプログラムがハングアップします。これは明らかに私が望むものではありません。私は別のスレッドでShowDialog()と呼ぶことができると思います。そのスレッドはハングアップしますが、スレッドは作業をしません。これは推奨される方法ですか?

私はまた、ウィンドウをまったく使用せず、その代わりにフルサイズのウィンドウ要素をページの他のものの前に置くことも考えました。これは私が開いている他のウィンドウがあることを除いて動作し、それらも開いているときにスプラッシュスクリーンを使用できるようにしたいと思います。ウィンドウ要素を使用した場合、すべてのウィンドウでそれを再作成する必要があり、私のカスタムスプラッシュクラスで便利なUpdateSplashTextメソッドを使用することはできません。

これは私に質問につながります。これを処理する正しい方法は何ですか?

お時間をありがとうと長い質問には申し訳ありませんが、詳細は重要です:)

答えて

1

あなたはあなたの進行状況ウィンドウのコンストラクタはTaskを取った後、ウィンドウがOnLoadedイベントにtask.Startを呼び出し、確保することができます。次に、親フォームからShowDialogを使用すると、進行状況ウィンドウがタスクを開始します。

ShowDialogを呼び出す前に、任意の場所にコンストラクタまたは親フォーム内のtask.Startを呼び出すこともできます。どちらがあなたにとって最も理にかなっています。

別のオプションは、メインウィンドウのステータスストリップにプログレスバーを使用し、ポップアップを取り除くことです。このオプションは、最近ますます普及しているようです。

1

WindowVisibilityプロパティを使用すると、スプラッシュ画面の実行中にウィンドウ全体を非表示にすることができます。

XAML

<Window ... Name="window" /> 

コード

window.Visibility = System.Windows.Visibility.Hidden; 

//show splash 
//do work 
//end splash 

window.Visibility = System.Windows.Visibility.Visible; 
+0

これは、このようなものと言えるでしょう。 "ShowDialog"を呼び出すことによって作成されたモダリティは完璧ですが、私は実行を停止しないために必要でした。 – BrianVPS

-1

私は別のスレッドでShowDialog()を呼び出すことによって、この作品を作るための方法を発見しました。私は仕事を処理する私のダイアログクラスで私自身のShowMe()HideMe()メソッドを作成しました。 Closingイベントをキャプチャして、ダイアログを閉じて再利用できるようにします。あなたはShowDialogは、あなたがしたいUIの挙動のほとんどを与えることを正しい

public partial class StartupSplash : Window 
{ 
    private Thread _showHideThread; 

    public StartupSplash() 
    { 
     InitializeComponent(); 

     this.Closing += OnCloseDialog; 
    } 


    public string Message 
    { 
     get 
     { 
      return this.lb_progress.Content.ToString(); 
     } 
     set 
     { 
      if (Application.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread) 
       this.lb_progress.Content = value; 
      else 
       this.lb_progress.Dispatcher.Invoke(new Action(() => this.lb_progress.Content = value)); 
     } 
    } 


    public void ShowMe() 
    { 
     _showHideThread = new Thread(new ParameterizedThreadStart(doShowHideDialog)); 
     _showHideThread.Start(true); 
    } 

    public void HideMe() 
    { 
     //_showHideThread.Start(false); 
     this.doShowHideDialog(false); 
    } 

    private void doShowHideDialog(object param) 
    { 
     bool show = (bool)param; 

     if (show) 
     { 
      if (Application.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread) 
       this.ShowDialog(); 
      else 
       Application.Current.Dispatcher.Invoke(new Action(() => this.ShowDialog())); 
     } 
     else 
     { 
      if (Application.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread) 
       this.Close(); 
      else 
       Application.Current.Dispatcher.Invoke(new Action(() => this.Close())); 
     } 
    } 


    private void OnCloseDialog(object sender, CancelEventArgs e) 
    { 
     e.Cancel = true; 
     this.Hide(); 
    } 
} 
+1

これはうまくいくでしょうが、すべてのUIコントロールがすべて同じスレッド上にあれば、おそらく長期的には問題が少なくなります。 –

+0

複数のUIスレッドは、本当に扱いが難しく、既存のUIフレームワーク全体でUIスレッドが1つしかないという前提のため、多くの微妙で難解な問題や問題を回避することができます。 – Servy

+0

私は通常、複数のUIスレッドを持つことは複雑ですが、この場合SplashScreenはすべてのセカンダリスレッドロジックを内部にカプセル化することに同意します。 ShowMeを呼び出し、ダイアログのテキストを変更するMessageプロパティを設定するだけです。呼び出し側は余分なスレッドを意識する必要はありません。それは非常に扱いやすいと私はそれがうまくいくと思う。私は確かにここで他の答えを調べるだろうが、今は私の締め切りが先週だったので移動しなければならない:( – BrianVPS

13

は、ここに私のスプラッシュ画面のクラスのための私のコードです。

それを呼び出すとすぐに実行をブロックするという問題があります。フォームを表示した後、コードを表示するにはどうすればよいでしょうか?それはあなたの問題だ。

あなたは、スプラッシュクラス内のすべての作業を行うことができますが、それは密接な結合のためにむしろ貧弱な方法です。

あなたができることはWindowLoadedイベントを利用して、ウィンドウが表示された後に実行する必要のあるコードを定義します。ただし、表示する前に定義する必要があります。

public static void DoWorkWithModal(Action<IProgress<string>> work) 
{ 
    SplashWindow splash = new SplashWindow(); 

    splash.Loaded += (_, args) => 
    { 
     BackgroundWorker worker = new BackgroundWorker(); 

     Progress<string> progress = new Progress<string>(
      data => splash.Text = data); 

     worker.DoWork += (s, workerArgs) => work(progress); 

     worker.RunWorkerCompleted += 
      (s, workerArgs) => splash.Close(); 

     worker.RunWorkerAsync(); 
    }; 

    splash.ShowDialog(); 
} 

あなたは進行状況インジケータを受け入れるすべての労働者の方法で渡すことができますし、一般的なスプラッシュ画面を見せながら、それがバックグラウンドスレッドでその仕事をするように、この方法は、ここでは定型的なコードをカプセル化するように設計されていることを注意それは労働者から指示された進歩を有する。私は、ダイアログが起動している間、私はちょうどそれが「無効」たかった隠したくなかった

public void Foo() 
{ 
    DoWorkWithModal(progress => 
    { 
     Thread.Sleep(5000);//placeholder for real work; 
     progress.Report("Finished First Task"); 

     Thread.Sleep(5000);//placeholder for real work; 
     progress.Report("Finished Second Task"); 

     Thread.Sleep(5000);//placeholder for real work; 
     progress.Report("Finished Third Task"); 
    }); 
} 
+0

+1しかし、あなたが 'worker 'を設定するさまざまなタスクに再利用できるように、' void Foo(Action <アクション>作業) –

+0

@DaxFohlもし私がこれまでに行っていたら、おそらく 'IProgress 'オブジェクトの代わりに渡すでしょう。 'Action ';それはセマンティクスをより良く運ぶのに役立ちます。 – Servy

+0

@DaxFohl Example編集中。 – Servy