6

SOに関するこのトピックにはいくつかの回答があることは知っていますが、私のために働く解決策を得ることができません。データ テンプレート内から起動された ICommand から、新しいウィンドウを開こうとしています。新しいウィンドウがインスタンス化されると(「new MessageWindowP」内で) 、次の両方で前述のエラーが発生します。

TPL/FromCurrentSynchronizationContext Update の使用: 動作します

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            var scheduler = TaskScheduler.FromCurrentSynchronizationContext();                  
            Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler);         
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

ThreadStart の使用: 更新: 推奨されません。Jon の回答を参照してください

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;

            var t = new ParameterizedThreadStart(CreateMessageWindow);
            var thread = new Thread(t);
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start(sender);           
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

ありがとう

編集。これまでの回答に基づいて、元のメソッドでコードを実行するだけでなく、現在のディスパッチャーで BeginInvoke も試したことを指摘したいと思います (それがコードの開始方法です)。下記参照:

BeginInvoke Update: 非推奨 Jon's answer を参照

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender);       
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

同じスレッド で更新: 既に UI スレッドを使用している場合に機能します

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            var messageP = new MessageWindowP();
            messageP.ViewModel.Participants.Add(user);
            messageP.View.Show();    
        }
    }

}

BeginInvoke、最初/メイン ウィンドウ Update のディスパッチャへの参照を使用: 動作

 public void Execute(object sender)
   {
       if (sender is UserC)
       {
            var user = (UserC)sender;
                    GeneralManager.MainDispatcher.BeginInvoke(
                               DispatcherPriority.Normal,
                               new Action(() => this.CreateMessageWindow(user)));      
        }
    }

GeneralManager.MainDispatcher は、最初に作成したウィンドウの Dispatcher への参照です。

     [somewhere far far away]
        mainP = new MainP();
        MainDispatcher = mainP.View.Dispatcher;

私は途方に暮れています。

4

3 に答える 3

7

呼び出しスレッドはSTA であるだけでなく、メッセージ ループも必要です。アプリケーションには、既にメッセージ ループを持っているスレッドが 1 つしかなく、それがメイン スレッドです。したがってDispatcher.BeginInvoke、メインスレッドからウィンドウを開くために使用する必要があります。

たとえば、メイン アプリケーション ウィンドウ ( MainWindow) への参照がある場合、次のことができます。

MainWindow.BeginInvoke(
    DispatcherPriority.Normal, 
    new Action(() => this.CreateMessageWindow(user)));

更新: 注意してください:やみくもに呼び出すことはできませDispatcher.CurrentDispatcherん。ドキュメントには次のように書かれていCurrentDispatcherます:

現在実行中のスレッドの Dispatcher を取得し、まだスレッドに関連付けられていない場合は新しい Dispatcher を作成します。

そのため、既存の UI コントロール (上記の例のメイン ウィンドウなど) に関連付けられた を使用する必要があります。Dispatcher

于 2012-04-26T14:03:42.553 に答える
5

TPL を使用すると、TPL Extras の StaTaskScheduler を使用できます。

STA スレッドでタスクを実行します。

COMにのみ使用しました。複数の UI スレッドを実行しようとしたことはありません。

于 2012-04-26T15:00:29.963 に答える
3

バックグラウンド スレッドからウィンドウを作成しようとしています。さまざまな理由でそれを行うことはできません。通常、メイン アプリケーション スレッドでウィンドウを作成する必要があります。

あなたの場合、コマンドはメイン(UI)スレッドから確実に起動するため、単純なアイデアは、を割り当てるのではなく、すぐに実行する(CreateMessageWindow内部で呼び出すだけ)ことです。実行するスレッドが不明な場合は、 を使用して UI スレッドにマーシャリングできます。ExecuteTaskExecuteDispatcher.BeginInvoke()

新しいウィンドウを非メイン スレッドで実行したい場合はほとんどありません。あなたのケースでこれが本当に必要な場合は、Dispatcher.Run();afterを追加する必要がありますmessageP.View.Show();(コードの 2 番目のバリアントを使用)。これにより、新しいスレッドでメッセージ ループが開始されます。

TPL のスレッドで window を実行しようとしないでください。これらのスレッドは通常、スレッドプール スレッドであり、制御できないためです。たとえば、それらが STA であることを保証することはできません (通常、それらは MTA です)。

編集:更新されたコードのエラーから、 UI 以外のスレッドで実行される
ことは明らかです。の代わりにExecute使ってみてください。(現在のスレッドのディスパッチャーを意味します。現在のスレッドがメインスレッドでない場合、これは間違っている可能性があります。)Application.Current.DispatcherDispatcher.CurrentDispatcherCurrentDispatcher

于 2012-04-26T14:04:48.847 に答える