3

私のアプリケーションでは、次のコードを使用して新しい UI スレッドを作成します。

Thread thread = new Thread(() =>
    {
        MyWindow windowInAnotherThread = new MyWindow();
        windowInAnotherThread.Show();
        System.Windows.Threading.Dispatcher.Run();
    }) { IsBackground = true };
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();

これにより、次のような問題が発生します。

MyWindow クラスのコンストラクターでは、BackgroundWorker が実行されます。RunWorkerCompleted では、BackgroundWorker が計算しているデータで Control を更新する必要があります。

これを示す小さなサンプルを作成しました。

public partial class MyWindow : Window {
    public MyWindow() {
        InitializeComponent();

        var bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync();
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        this.Title = "Calculated title";
    }

    void bw_DoWork(object sender, DoWorkEventArgs e) {
        Thread.Sleep(3000);
    }
}

(別のスレッドが所有しているためbw_RunWorkerCompleted()InvalidOperationException呼び出し元のスレッドはこのオブジェクトにアクセスできません。) BackgroundWorker が開始元の正しい UI スレッドに戻っていないようです。

この問題を解決するために何ができるか、誰かが私を助けることができますか? 私が使用しているフレームワーク内にあるため、BackgroundWorker を実行しているコードを変更することはできません。しかし、RunWorkerCompleted-Event で別のことができます。しかし、この問題を解決する方法がわかりません。

4

6 に答える 6

2

同様の問題に遭遇しました。以下の注 1 と 2 に基づいて、UIBackgroundWorker を作成しました。この問題に遭遇した他の開発者を助けることができるかもしれません.

動作する場合は、他の開発者のためにデザインをお知らせいただくか、更新してください。

public class UIBackgroundWorker : BackgroundWorker
{

    private System.Windows.Threading.Dispatcher uiDispatcher;
    public SafeUIBackgroundWorker(System.Windows.Threading.Dispatcher uiDispatcher)
        : base()
    {
        if (uiDispatcher == null)
            throw new Exception("System.Windows.Threading.Dispatcher instance required while creating UIBackgroundWorker");
        else
            this.uiDispatcher = uiDispatcher;
    }

    protected override void OnProgressChanged(ProgressChangedEventArgs e)
    {
        if (uiDispatcher.CheckAccess())
            base.OnProgressChanged(e);
        else
            uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnProgressChanged(e)));
    }

    protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
    {
        if (uiDispatcher.CheckAccess())
            base.OnRunWorkerCompleted(e);
        else
            uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnRunWorkerCompleted(e)));
    }
}
于 2012-09-18T06:22:12.977 に答える
1

問題は、をセットアップする必要があることですSynchronizationContext。これは通常は問題にはなりませんが、コンストラクター ( の前に起動される) でDispatcher.Invoke使用しているため、コンテキストはセットアップされません。BackgroundWorkerDispatcher.Run

スレッドの作成を次のように変更します。

Thread thread = new Thread(() =>
    {
        // Create the current dispatcher (done via CurrentDispatcher)
        var dispatcher = Dispatcher.CurrentDispatcher;
        // Set the context
        SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(dispatcher));

        MyWindow windowInAnotherThread = new MyWindow();
        windowInAnotherThread.Show();
        Dispatcher.Run();
    });

thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();

SynchronizationContextこれにより、ウィンドウの構築前に が配置されるため、ウィンドウが正しく実行されます。

于 2011-11-08T22:48:48.070 に答える
0

内部にgetterandを指定してみてsetterください。そして、setter メソッドを介して BackgroundWorker オブジェクトを Mywindow に渡します。これで問題は解決するはずです。BackgroundWorkerMyWindow

于 2011-11-08T22:18:55.407 に答える
0

呼び出し元の関数でデリゲート メソッドと呼び出しを使用する必要があります。ここに良い例があります: http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

コードを使用して、

    public partial class MyWindow : Window {


    delegate void TitleSetter(string title);

    public MyWindow() {
            InitializeComponent();

        var bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync();
    }

    void SetTitle(string T)
    {
      this.Title = T;
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {

      try    
        {
        TitleSetter T = new TitleSetter(SetTitle);
        invoke(T, new object[]{"Whatever the title should be"}); //This can fail horribly, need the try/catch logic.
        }catch (Exception){}
    }

    void bw_DoWork(object sender, DoWorkEventArgs e) {
        Thread.Sleep(3000);
    }
}
于 2011-11-08T22:19:05.987 に答える