ViewModel类是否应该实现INotifyPropertyChanged,还是应该使用对象组合?

时间:2022-04-26 06:53:30

I'm developing a MVVM WPF application with C# and .NET Framework 4.6.

我正在使用c#和。net Framework 4.6开发一个MVVM WPF应用程序。

I have this class:

我有这个类:

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

I have implemented here INotifyPropertyChanged because I don't want to implement it in all of my ViewModel classes.

我在这里实现了INotifyPropertyChanged,因为我不想在所有的ViewModel类中实现它。

To use this class, I use inherit:

要使用这个类,我使用继承:

public class Presenter : ObservableObject
{
    private string _someText;

    public string SomeText
    {
        get { return _someText; }
        set
        {
            _someText = value;
            RaisePropertyChangedEvent("SomeText");
        }
    }
}

But, is there a way to use ObservableObject using object composition?

但是,有没有一种方法可以用对象组合来使用可观察对象?

I understand object composition as instead of inherit, create a private object instance of ObservableObject in class Presenter.

我理解对象组合不是继承,而是在class Presenter中创建一个可观察对象的私有对象实例。

I'm not sure if any ViewModel class should implement INotifyPropertyChanged.

我不确定任何ViewModel类是否应该实现INotifyPropertyChanged。

UPDATE:
This is not a duplicate question. I'm asking if a ViewModel has always to implement INotifyPropertyChanged interface or instead, I can use Object composition. I have explained before. Please, read carefully my question.

更新:这不是一个重复的问题。我在问ViewModel是否总是实现INotifyPropertyChanged接口,或者我可以使用对象组合。我以前解释道。请仔细阅读我的问题。

2 个解决方案

#1


0  

Well, ... ViewModels are best to be viewed as Composition, but the notification part should be an implementation of the Interface (or in your case inheritance). You have your DTOs and your ViewModels would be a composition of the DTOs, depending on the scenario.

好吧,…视图模型最好被视为组合,但是通知部分应该是接口的实现(或者在您的案例继承中)。您有dto,您的viewmodel将是dto的组合,这取决于场景。

This implementation of the same stuff can be tedious, but for WPF is still necessary. What you can do to simplify the process is to use Veawrs like Fody. It changes your observation of the ViewModel. All of the properties of the VM are by default observable properties, but you exclude the ones you do not want to, or you can define for one property to let the UI know that it should also update others.

实现相同内容可能会很麻烦,但是对于WPF来说仍然是必要的。要简化这个过程,可以使用像Fody这样的Veawrs。它改变了您对ViewModel的观察。VM的所有属性都是默认的可观察属性,但是您排除了不想要的属性,或者您可以定义一个属性,让UI知道它也应该更新其他属性。

It keeps the code very clean and simple. You will not need to implement the Interface, but it will be inherited in build time if you give the class the needed attribute.

它使代码非常干净和简单。您将不需要实现接口,但是如果您为类提供所需的属性,则将在构建时继承接口。

#2


0  

If you want to improve the robustness by avoiding code duplication and using strings in code. Then you could use the following base class.

如果您希望通过避免代码复制和在代码中使用字符串来提高健壮性。然后可以使用下面的基类。

I don't know an approach with composition, but this one is the best I know. Since it has some additional benefits (Cloning made easy a.s.o.)

我不知道作曲的方法,但这是我所知道的最好的方法。因为它有一些额外的好处(克隆使a.s.o变得容易)。


public abstract class BindingBase : INotifyPropertyChanged
{
    private IDictionary<string, object> _backingFields;
    private IDictionary<string, object> BackingFields
    {
        get { return _backingFields ?? (_backingFields = new Dictionary<string, object>(); }
    }    
protected T GetValue<T>(Expression<Func<T>gt; expr) { var name = GetName(expr); return BackingFields.Contains(name) ? (T)BackingFields[name].Value : default(T); }
protected void SetValue<T>(Expression<Func<T>gt; expr, T value) { var name = GetName(expr); if (BackingFields.Contains(name) && BackingFields[name].Value.Equals(value)) return; // return without doing anything, since the value is not changing
BackingFields[name] = value; RaisePropertyChanged(name); }
private void RaisePropertyChanged(string name) { // you know this part
}
private string GetName (Expression<Func<T> expr) { // implementation can be found via google } }
Usage is quite easy.
public class BindingChild : BindingBase
{
    public string SampleProperty
    {
        get { return GetValue(() => SampleProperty); }
        set { SetValue(() => SampleProperty, value); }
    }
}

#1


0  

Well, ... ViewModels are best to be viewed as Composition, but the notification part should be an implementation of the Interface (or in your case inheritance). You have your DTOs and your ViewModels would be a composition of the DTOs, depending on the scenario.

好吧,…视图模型最好被视为组合,但是通知部分应该是接口的实现(或者在您的案例继承中)。您有dto,您的viewmodel将是dto的组合,这取决于场景。

This implementation of the same stuff can be tedious, but for WPF is still necessary. What you can do to simplify the process is to use Veawrs like Fody. It changes your observation of the ViewModel. All of the properties of the VM are by default observable properties, but you exclude the ones you do not want to, or you can define for one property to let the UI know that it should also update others.

实现相同内容可能会很麻烦,但是对于WPF来说仍然是必要的。要简化这个过程,可以使用像Fody这样的Veawrs。它改变了您对ViewModel的观察。VM的所有属性都是默认的可观察属性,但是您排除了不想要的属性,或者您可以定义一个属性,让UI知道它也应该更新其他属性。

It keeps the code very clean and simple. You will not need to implement the Interface, but it will be inherited in build time if you give the class the needed attribute.

它使代码非常干净和简单。您将不需要实现接口,但是如果您为类提供所需的属性,则将在构建时继承接口。

#2


0  

If you want to improve the robustness by avoiding code duplication and using strings in code. Then you could use the following base class.

如果您希望通过避免代码复制和在代码中使用字符串来提高健壮性。然后可以使用下面的基类。

I don't know an approach with composition, but this one is the best I know. Since it has some additional benefits (Cloning made easy a.s.o.)

我不知道作曲的方法,但这是我所知道的最好的方法。因为它有一些额外的好处(克隆使a.s.o变得容易)。


public abstract class BindingBase : INotifyPropertyChanged
{
    private IDictionary<string, object> _backingFields;
    private IDictionary<string, object> BackingFields
    {
        get { return _backingFields ?? (_backingFields = new Dictionary<string, object>(); }
    }    
protected T GetValue<T>(Expression<Func<T>gt; expr) { var name = GetName(expr); return BackingFields.Contains(name) ? (T)BackingFields[name].Value : default(T); }
protected void SetValue<T>(Expression<Func<T>gt; expr, T value) { var name = GetName(expr); if (BackingFields.Contains(name) && BackingFields[name].Value.Equals(value)) return; // return without doing anything, since the value is not changing
BackingFields[name] = value; RaisePropertyChanged(name); }
private void RaisePropertyChanged(string name) { // you know this part
}
private string GetName (Expression<Func<T> expr) { // implementation can be found via google } }
Usage is quite easy.
public class BindingChild : BindingBase
{
    public string SampleProperty
    {
        get { return GetValue(() => SampleProperty); }
        set { SetValue(() => SampleProperty, value); }
    }
}