Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从另一个线程修改对象属性时引发OnPropertyChanged_C#_.net_Multithreading_Winforms - Fatal编程技术网

C# 从另一个线程修改对象属性时引发OnPropertyChanged

C# 从另一个线程修改对象属性时引发OnPropertyChanged,c#,.net,multithreading,winforms,C#,.net,Multithreading,Winforms,所以我有一个目标: public class SomeObject: INotifyPropertyChanged { public decimal AlertLevel { get { return alertLevel; } set { if(alertLevel == value) return; aler

所以我有一个目标:

public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

         private void OnPropertyChanged(string propertyName) {
            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
}

假设我在非GUI线程的线程上更改此对象。当我没有对此类中任何GUI组件的引用时,如何让此对象在与GUI相同的线程上引发PropertyChanged事件?

对于WPF-添加以下引用:

PresentationFramework.dll
WindowsBase.dll
在后台线程中,将需要访问UI的代码包装到dispatcher.Invoke()中

供WinForms使用

  Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(() => {
       //Do Your magic here
   }));

通常,事件订阅者应负责在必要时将调用编组到UI线程

但是,如果所讨论的类是特定于UI的(也称为视图模型),则只要在UI线程上创建它,您就可以捕获同步上下文,并使用它引发如下事件:

public class SomeObject : INotifyPropertyChanged
{
    private SynchronizationContext syncContext;

    public SomeObject()
    {
        syncContext = SynchronizationContext.Current;
    }

    private decimal alertLevel;

    public decimal AlertLevel
    {
        get { return alertLevel; }
        set
        {
            if (alertLevel == value) return;
            alertLevel = value;
            OnPropertyChanged("AlertLevel");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            if (syncContext != null)
                syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
            else
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
           GUIThreadDispatcher.Instance.Init();  //setup the ability to use the GUI Thread when needed via a static reference
           Application.Run(new MainForm());
        }
    }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                GUIThreadDispatcher.Instance.BeginInvoke(() => {
                    if (PropertyChanged != null)
                         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init(this);  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
              GUIThreadDispatcher.Instance.BeginInvoke(() => {
              if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init();  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                  GUIThreadDispatcher.Instance.BeginInvoke(() => {
                     if (PropertyChanged != null)
                          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
或者,您可以通过构造函数传递同步上下文


另一种方法是保持对象的完整性,但数据通过中间同步绑定源绑定到它,如这里所述。

这是一个干净的实现(相对而言)。只是必须包含一个对
WindowsBase.dll
的引用,它原来是一个WPF库,所以呃…,对它不是很满意,但它是一个解决方案…:

 public class GUIThreadDispatcher {
        private static volatile GUIThreadDispatcher itsSingleton;
        private Dispatcher itsDispatcher;

        private GUIThreadDispatcher() { }
        public static GUIThreadDispatcher Instance
        {
            get
            {
                if (itsSingleton == null)
                    itsSingleton = new GUIThreadDispatcher();

                return itsSingleton;
            }
        }

        public void Init() {
            itsDispatcher = Dispatcher.CurrentDispatcher;
        }

        public object Invoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
           return itsDispatcher.Invoke(method, priority, args);
        }

        public DispatcherOperation BeginInvoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
            return itsDispatcher.BeginInvoke(method, priority, args);
        }
然后像这样初始化它:

public class SomeObject : INotifyPropertyChanged
{
    private SynchronizationContext syncContext;

    public SomeObject()
    {
        syncContext = SynchronizationContext.Current;
    }

    private decimal alertLevel;

    public decimal AlertLevel
    {
        get { return alertLevel; }
        set
        {
            if (alertLevel == value) return;
            alertLevel = value;
            OnPropertyChanged("AlertLevel");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            if (syncContext != null)
                syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
            else
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
           GUIThreadDispatcher.Instance.Init();  //setup the ability to use the GUI Thread when needed via a static reference
           Application.Run(new MainForm());
        }
    }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                GUIThreadDispatcher.Instance.BeginInvoke(() => {
                    if (PropertyChanged != null)
                         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init(this);  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
              GUIThreadDispatcher.Instance.BeginInvoke(() => {
              if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init();  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                  GUIThreadDispatcher.Instance.BeginInvoke(() => {
                     if (PropertyChanged != null)
                          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}

一个更好的主意,不使用任何WPF引用:

public class GUIThreadDispatcher {
        private static volatile GUIThreadDispatcher itsSingleton;
        private WeakReference itsDispatcher;

        private GUIThreadDispatcher() { }

        public static GUIThreadDispatcher Instance
        {
            get
            {
                if (itsSingleton == null)
                    itsSingleton = new GUIThreadDispatcher();

                return itsSingleton;
            }
        }

        public void Init(Control ctrl) {
            itsDispatcher = new WeakReference(ctrl);
        }

        public void Invoke(Action method) {
            ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: false));
        }

        public void BeginInvoke(Action method) {
            ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: true));
        }

        private void ExecuteAction(Action<Control> action) {
            if (itsDispatcher.IsAlive) {
                var ctrl = itsDispatcher.Target as Control;
                if (ctrl != null) {
                    action(ctrl);
                }
            }
        }

        public static void DoInGuiThread(Control ctrl, Action action, bool forceBeginInvoke = false) {
            if (ctrl.InvokeRequired) {
                if (forceBeginInvoke)
                    ctrl.BeginInvoke(action);
                else
                    ctrl.Invoke(action);
            }
            else {
                action();
            }
        }
    }
}
然后像这样使用:

public class SomeObject : INotifyPropertyChanged
{
    private SynchronizationContext syncContext;

    public SomeObject()
    {
        syncContext = SynchronizationContext.Current;
    }

    private decimal alertLevel;

    public decimal AlertLevel
    {
        get { return alertLevel; }
        set
        {
            if (alertLevel == value) return;
            alertLevel = value;
            OnPropertyChanged("AlertLevel");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            if (syncContext != null)
                syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
            else
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
           GUIThreadDispatcher.Instance.Init();  //setup the ability to use the GUI Thread when needed via a static reference
           Application.Run(new MainForm());
        }
    }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                GUIThreadDispatcher.Instance.BeginInvoke(() => {
                    if (PropertyChanged != null)
                         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init(this);  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
              GUIThreadDispatcher.Instance.BeginInvoke(() => {
              if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init();  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                  GUIThreadDispatcher.Instance.BeginInvoke(() => {
                     if (PropertyChanged != null)
                          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}

找到了一个更好的答案,而不必使用对表单控件的WeakReference,也不必使用WPF引用基于和Ivan的上述答案:

   public class GUIThreadDispatcher {
    private static volatile GUIThreadDispatcher itsSingleton;
    private SynchronizationContext itsSyncContext;

    private GUIThreadDispatcher() {}

    /// <summary>
    /// This needs to be called on the GUI Thread somewhere
    /// </summary>
    public void Init() {
        itsSyncContext = AsyncOperationManager.SynchronizationContext;
    }

    public static GUIThreadDispatcher Instance
    {
        get
        {
            if (itsSingleton == null)
                itsSingleton = new GUIThreadDispatcher();

            return itsSingleton;
        }
    }

    public void Invoke(Action method) {
        itsSyncContext.Send((state) => { method(); }, null);
    }

    public void BeginInvoke(Action method) {
        itsSyncContext.Post((state) => { method(); }, null);
    }
}
然后像这样使用:

public class SomeObject : INotifyPropertyChanged
{
    private SynchronizationContext syncContext;

    public SomeObject()
    {
        syncContext = SynchronizationContext.Current;
    }

    private decimal alertLevel;

    public decimal AlertLevel
    {
        get { return alertLevel; }
        set
        {
            if (alertLevel == value) return;
            alertLevel = value;
            OnPropertyChanged("AlertLevel");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            if (syncContext != null)
                syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
            else
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
           GUIThreadDispatcher.Instance.Init();  //setup the ability to use the GUI Thread when needed via a static reference
           Application.Run(new MainForm());
        }
    }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                GUIThreadDispatcher.Instance.BeginInvoke(() => {
                    if (PropertyChanged != null)
                         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init(this);  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
              GUIThreadDispatcher.Instance.BeginInvoke(() => {
              if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}
  private void MainForm_Load(object sender, EventArgs e) {

     //setup the ability to use the GUI Thread when needed via a static reference
     GUIThreadDispatcher.Instance.Init();  
     ...
  }
public class SomeObject: INotifyPropertyChanged
{
         public decimal AlertLevel {
            get {
                return alertLevel;
            }
            set {
                if(alertLevel == value) return;
                alertLevel = value;
                OnPropertyChanged("AlertLevel");
            }

          private void OnPropertyChanged(string propertyName) {
                  GUIThreadDispatcher.Instance.BeginInvoke(() => {
                     if (PropertyChanged != null)
                          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                });
        }}

您必须使用Dispatcher。检查这个是的,我看到过类似的,但是Dispatcher类在哪里?我需要哪种参考资料?我在任何地方都找不到它…您需要向
PresentationFramework.dll
添加一个引用,通常它只用于WPF,但dispatcher类同时适用于winforms和WPF。IntelisSense没有显示它。尝试粘贴代码并编译。非常奇怪。。也许你是从单元测试中调用它?应用程序启动了吗?是Winforms的WPF吗?这仍然不起作用。。。我知道原因了:我的类的问题是它是bindinglist中的一个元素。所以你提供的文章很有趣。我第一次读的时候没有同步,但现在看到了。发布了一个我最终使用的答案,现在意识到它与您的答案非常相似。我不能100%确定
调度程序。如果您在
应用程序之前调用它,CurrentDispatcher
将具有正确的值。运行(
。它似乎可以工作,但我明白您的意思。应该已经在MainForm\u Load中初始化了。。。