Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.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# 模型由后台线程更新,如何获取对ViewModel的更改?_C#_Wpf_Mvvm - Fatal编程技术网

C# 模型由后台线程更新,如何获取对ViewModel的更改?

C# 模型由后台线程更新,如何获取对ViewModel的更改?,c#,wpf,mvvm,C#,Wpf,Mvvm,我有一个模型可以在后台自我更新。目前我在模型上使用INotifyPropertyChanged,但有人告诉我这是个坏主意,因为模型应该是独立于UI的 当模型在MVVM设计模式中发生更改时,是否有用于更新ViewModel的首选模式 该模型应该是独立于用户界面的 …模型当然不应该知道视图,这就是为什么您要让模型(或ViewModel)实现INotifyPropertyChanged,这样视图就可以绑定到模型(或VM)上的属性,并让框架的更改通知通知视图更改(这样您的模型就不需要) 当然,如果您要根

我有一个模型可以在后台自我更新。目前我在模型上使用INotifyPropertyChanged,但有人告诉我这是个坏主意,因为模型应该是独立于UI的

当模型在MVVM设计模式中发生更改时,是否有用于更新ViewModel的首选模式

该模型应该是独立于用户界面的

…模型当然不应该知道视图,这就是为什么您要让模型(或ViewModel)实现INotifyPropertyChanged,这样视图就可以绑定到模型(或VM)上的属性,并让框架的更改通知通知视图更改(这样您的模型就不需要)


当然,如果您要根据后台线程的数据对UI进行更改,则需要将它们安全地分派到UI线程-您可以使用标准的.net线程机制(如BackgroundWorker),也可以使用WPF的类。

ViewModel的目的是允许模型独立于UI

只需让viewmodel侦听来自模型的事件

public MyViewModel(IView view, IModel model) {
    model.SomeEvent += HandleSomeEvent;
    ....
}
如果要发送INotifyPropertyChanged,需要先切换到uithread

(如果您的模型比viewmodel寿命长,您应该查看一些弱引用事件模式,以允许GC清理viewmodel)

两条注释:

  • INotifyPropertyChanged没有本质上特定于UI的内容。这只是一个界面。您可以自由地在模型对象上实现它,这没有什么错
  • 如果WPF绑定到ViewModel上的一个属性(Foo),并且您甚至在另一个线程上触发了PropertyChanged事件,那么WPF实际上会在GUI线程上调用该属性的getter,这样您就不必处理Dispatcher警告:如果执行此操作,请确保属性的访问器是线程安全的
  • 例如:

    class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    
        public MyViewModel(MyModel mdl)
        {
            mdl.PropertyChanged += 
                new PropertyChangedEventHandler(
                    mdl_PropertyChanged);
            _mdl = mdl;
        }
    
        private MyModel _mdl = null;
    
        void mdl_PropertyChanged(object sender, 
            PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Foo")
            {
                this.Foo = _mdl.Foo;
            }
        }
    
        public int Foo
        {
            get
            {
                lock(_foo_Lock)
                {
                    return _foo;
                }
            }
            set
            {
                lock(_foo_Lock)
                {
                    _foo = value;
                }
                NotifyPropertyChanged("Foo");
            }
        }
        private readonly object _foo_Lock = new object();
        private int _foo = 0;
    }
    
    编辑:我实际上并不建议使用硬编码字符串作为属性名称。下面是一个示例,您可以使用它在使用反射的构造过程中获取属性名称。然后创建一个基类。然后,您可以从AbstractViewModel继承并实现如下属性:

        #region IsCheckable
        public bool IsCheckable
        {
            get
            {
                lock(m_IsCheckable_Lock)
                {
                    return m_IsCheckable;
                }
            }
            protected set
            {
                bool fire = false;
                lock(m_IsCheckable_Lock)
                {
                    if (m_IsCheckable != value)
                    {
                        m_IsCheckable = value;
                        fire = true;
                    }
                }
                if(fire)
                {
                    NotifyPropertyChanged(m_IsCheckableArgs);
                }
            }
        }
        private readonly object m_IsCheckable_Lock = new object();
        private bool m_IsCheckable = false;
        static readonly PropertyChangedEventArgs m_IsCheckableArgs =
            NotifyPropertyChangedHelper.CreateArgs<MyViewModel>(o => 
                    o.IsCheckable);
        #endregion
    
    #区域是可检查的
    公共场所是可以检查的
    {
    收到
    {
    锁(m_可检查锁)
    {
    返回m_是可检查的;
    }
    }
    保护集
    {
    布尔火=假;
    锁(m_可检查锁)
    {
    如果(m_可检查!=值)
    {
    m_可检查=值;
    火=真;
    }
    }
    如果(火灾)
    {
    NotifyPropertyChanged(m_正在检查);
    }
    }
    }
    私有只读对象m_可检查_Lock=new object();
    私有布尔m_可检查=false;
    静态只读属性ChangedEventArgs m_正在检查=
    NotifyPropertyChangedHelper.CreateArgs(o=>
    o、 不可检查);
    #端区
    
    这就是我对对象进行编码的方式。我确信可以继续使用此“模式”。“如果要发送INotifyPropertyChanged,需要先切换到uithread。”--这不正确。对于使用INotifyPropertyChanged的简单属性绑定,WPF会自动将UI更新发送到UI线程。不需要在UI线程中引发
    PropertyChanged
    事件。例如,请参见该问题的公认答案。@PeterDuniho,我已经很久没有处理过这个问题了,所以我可能完全离开了这里,但我不明白WPF在发起活动时是如何发挥作用的?公认的答案是WPF将在UI线程上调用getter,但这并不意味着在UI线程上调用PropertyChanged事件。如果模型以某种方式进行了
    PropertyChanged(这是新的PropertyChangedArgs(“prop”))
    ,则绑定到事件的处理程序将在当前线程上调用。这可能不是问题,但它不同于您期望在UI线程上发生的常规PropertyChanged处理。“这并不意味着PropertyChanged事件是在UI线程上调用的”——我从来没有这样写过。我写的是不需要这样。引发事件时,WPF将处理将其事件处理移动到UI线程的操作。(正如您在回答中所写的)“您需要首先切换到uithread”这一点并不正确。@PeterDuniho,INotifyPropertyChange没有声明“必须在UI线程上调用PropertyChanged”,但由于WPF为您这样做,这很可能是您的意图。还要注意的是,像UWP这样的其他框架要求调用者分派到正确的线程。“因为WPF为您做”——同样,WPF不会在UI线程上引发事件。它处理UI线程上的事件。这是一个非常显著的差异。这里的要点是,发布的问题是关于WPF的,在这种情况下,“您需要先切换到uithread”的说法是不正确的。