3

INotifyPropertyChanged を実装するオブジェクトをスレッド セーフに保つにはどうすればよいでしょうか。オブジェクトをシリアル化できるようにする必要があるため、SynchronizationContext を使用できません。

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
           // What can I add here to make it thread-safe? 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
4

4 に答える 4

7

ですから...コンパイル時にコードを生成する拡張機能に頼ることを気にしないのであれば、実際には非常に良い方法があることがわかります。とにかく Fody/PropertyChanged を使用していたので、これは非常に簡単に変更できました。これによりSynchronizationContext、実際には UI を認識していない in モデルを参照する必要がなくなります。

  1. まず、NuGet から入手できるPropertyChanged.Fodyをインストールします。

  2. 現在実装しているすべてのクラスINofityPropertyChangedには、代わりに属性が必要です[ImplementPropertyChanged]。手動の実装は削除する必要があります。詳細については、readmewikiを参照してください。

  3. PropertyChangedNotificationInterceptorを実装する必要があります。wiki で WPF の例を提供しています。WinForms プロジェクトの私の実装:

    public static class PropertyChangedNotificationInterceptor
    {
        public static SynchronizationContext UIContext { get; set; }
    
        public static void Intercept(object target, Action onPropertyChangedAction, string propertyName)
        {
            if (UIContext != null)
            {
                UIContext.Post(_ =>
                {
                    onPropertyChangedAction();
                }, null);
            }
            else
            {
                onPropertyChangedAction();
            }
        }
    }
    
  4. PropertyChangedNotificationInterceptor.UIContextどこかに設定します。PropertyChangedNotificationInterceptor.UIContext = WindowsFormsSynchronizationContext.Current;メインフォームのコンストラクターに入れることができます。

    FormコンストラクターはControl コンストラクターを通過し、最終的にWindowsFormsSynchronizationContextまだ存在しない場合は作成します。したがって、.Currentここをキャプチャしても安全です。(注: これは実装の詳細である可能性があり、将来の .NET バージョンや別のプラットフォームなどで変更される可能性があります。)

これは、単一の同期コンテキスト (単一の UI スレッド) しかない場合にのみ機能することに注意してください。複数の UI スレッドが混乱し、複数の UI スレッドでデータバインディングが必要になった場合、これははるかに複雑になります。ですから、そうしないでください。

これを書いてくれて、この特定の機能を見つけるのを手伝ってくれたSimon Croppに感謝します。PropertyChanged

于 2014-12-17T02:40:16.203 に答える
5

WPFを使用している場合は、ディスパッチャーを使用してUIスレッドへの呼び出しをマーシャリングできます。

App.Current.Dispatcher.Invoke(new Action(()=>{ 
    if (handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName));
}));
于 2012-04-12T19:41:46.813 に答える
5

運が悪く、Winforms を使用する必要がある場合は、アプリケーションの MainForm を使用して UI スレッドでハンドラーを呼び出してみてください。悪い点は、含める必要があることです

using System.Windows.Forms;

protected void OnPropertyChanged(string propertyName)
{
    var handler = PropertyChanged;
    if (handler != null)
    {
        if (Application.OpenForms.Count == 0) return; 
        var mainForm = Application.OpenForms[0];
        if(mainForm == null) return; // No main form - no calls

        if (mainForm.InvokeRequired) 
        {
            // We are not in UI Thread now
            mainform.Invoke(handler, new object[] {
               this, new PropertyChangedEventArgs(propName)});
        }
        else
        {
            handler(this, new PropertyChangedEventArgs(propertyName)); 
        }              
    }
}
于 2013-11-26T16:11:15.367 に答える