12

現在、コンポーネントの一部を UI スレッドで実行するコンポーネントを作成しようとしています (説明が長すぎます)。したがって、最も簡単な方法は、コントロールをそれに渡し、InvokeRequired/Invoke を使用することです。しかし、コントロール参照を「データ/背景」コンポーネントに渡すのは良い設計だとは思わないので、コントロールを使用可能にする必要なしに UI スレッドでコードを実行する方法を探しています。 . WPF の Application.Dispatcher.Invoke のようなもの...

任意のアイデア、thx マーティン

4

5 に答える 5

19

これを行うには、WinForms と WPF の両方で機能する、より優れた、より抽象的な方法があります。

System.Threading.SynchronizationContext.Current.Post(theMethod, state);

これは、WindowsForms がWindowsFormsSynchronizationContext現在の同期コンテキストとしてオブジェクトをインストールするために機能します。WPF は、独自の特殊な同期コンテキスト ( ) をインストールして、同様のことを行いますDispatcherSynchronizationContext

.Postに対応しcontrol.BeginInvoke、 に.Send対応しcontrol.Invokeます。

于 2009-02-02T23:03:32.987 に答える
3

まず、フォーム コンストラクターで、SynchronizationContext.Currentオブジェクト (実際にはWindowsFormsSynchronizationContext) へのクラス スコープの参照を保持します。

public partial class MyForm : Form {
    private SynchronizationContext syncContext;
    public MyForm() {
        this.syncContext = SynchronizationContext.Current;
    }
}

次に、クラス内の任意の場所で、このコンテキストを使用してメッセージを UI に送信します。

public partial class MyForm : Form {
    public void DoStuff() {
        ThreadPool.QueueUserWorkItem(_ => {
            // worker thread starts
            // invoke UI from here
            this.syncContext.Send(() =>
                this.myButton.Text = "Updated from worker thread");
            // continue background work
            this.syncContext.Send(() => {
                this.myText1.Text = "Updated from worker thread";
                this.myText2.Text = "Updated from worker thread";
            });
            // continue background work
        });
    }
}

ラムダ式を操作するには、次の拡張メソッドが必要です: http://codepaste.net/zje4k6

于 2012-02-24T15:18:14.533 に答える
2

そうです、コントロールをスレッドに渡すのは良くありません。Winforms コントロールはシングルスレッドであり、それらを複数のスレッドに渡すと、競合状態が発生したり、UI が壊れたりする可能性があります。代わりに、スレッドの機能を UI で使用できるようにし、UI の準備が整ったときにスレッドが呼び出されるようにする必要があります。バックグラウンド スレッドで UI の変更をトリガーする場合は、バックグラウンド イベントを公開し、UI からサブスクライブします。スレッドはいつでもイベントを発生させることができ、UI は可能なときにイベントに応答できます。

UI スレッドをブロックしないスレッド間の双方向通信を作成するには、多くの作業が必要です。以下は、BackgroundWorker クラスを使用した非常に簡略化された例です。

public class MyBackgroundThread : BackgroundWorker
{
    public event EventHandler<ClassToPassToUI> IWantTheUIToDoSomething;

    public MyStatus TheUIWantsToKnowThis { get { whatever... } }

    public void TheUIWantsMeToDoSomething()
    {
        // Do something...
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        // This is called when the thread is started
        while (!CancellationPending)
        {
            // The UI will set IWantTheUIToDoSomething when it is ready to do things.
            if ((IWantTheUIToDoSomething != null) && IHaveUIData())
                IWantTheUIToDoSomething( this, new ClassToPassToUI(uiData) );
        }
    }
}


public partial class MyUIClass : Form
{
    MyBackgroundThread backgroundThread;

    delegate void ChangeUICallback(object sender, ClassToPassToUI uiData);

    ...

    public MyUIClass
    {
        backgroundThread = new MyBackgroundThread();

        // Do this when you're ready for requests from background threads:
        backgroundThread.IWantTheUIToDoSomething += new EventHandler<ClassToPassToUI>(SomeoneWantsToChangeTheUI);

        // This will run MyBackgroundThread.OnDoWork in a background thread:
        backgroundThread.RunWorkerAsync();
    }


    private void UserClickedAButtonOrSomething(object sender, EventArgs e)
    {
        // Really this should be done in the background thread,
        // it is here as an example of calling a background task from the UI.
        if (backgroundThread.TheUIWantsToKnowThis == MyStatus.ThreadIsInAStateToHandleUserRequests)
            backgroundThread.TheUIWantsMeToDoSomething();

        // The UI can change the UI as well, this will not need marshalling.
        SomeoneWantsToChangeTheUI( this, new ClassToPassToUI(localData) );
    }

    void SomeoneWantsToChangeTheUI(object sender, ClassToPassToUI uiData)
    {
        if (InvokeRequired)
        {
            // A background thread wants to change the UI.
            if (iAmInAStateWhereTheUICanBeChanged)
            {
                var callback = new ChangeUICallback(SomeoneWantsToChangeTheUI);
                Invoke(callback, new object[] { sender, uiData });
            }
        }
        else
        {
            // This is on the UI thread, either because it was called from the UI or was marshalled.
            ChangeTheUI(uiData)
        }
    }
}
于 2009-01-20T20:08:57.290 に答える
1

操作するフォームのメソッドに UI 操作を配置し、APM のように、バックグラウンド スレッドで実行されるコードにデリゲートを渡します。を使用する必要はありませんparams object p。目的に合わせて強く型付けできます。これは単なる一般的なサンプルです。

delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
  if (InvokeRequired) 
    BeginInvoke(d,p);        
  else
  {
    //do stuff to UI
  }
}

このアプローチは、デリゲートが特定のインスタンスのメソッドを参照するという事実に基づいています。実装をフォームのメソッドにすることで、フォームを としてスコープに入れますthis。以下は意味的に同じです。

delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
  if (this.InvokeRequired) 
    this.BeginInvoke(d,p);        
  else
  {
    //do stuff to UI
  }
}
于 2009-01-28T05:06:24.917 に答える