0

I'm doing and WPF application and I have a ViewModel that I use in several Views and in DataGrids.

Now I have another View that requires an extended or decorated version of that ViewModel. So I decided to go for inheritance in this way:

public class StandardViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class ExtendedViewModel : StandardViewModel
{
    public string Email { get; set; }
}

However, I want to decorate and existing instance of the StandardViewModel. Specifically the selected object in the DataGrid so it can be passed into the other View.

The new View needs access to the properties of both classes (the Email and the FirtsName and LastName)

So I'm thinking of ways to creating a constructor for my ExtendedViewModel.

My idea is to copy the base instance directly.

  • Is this correct?
  • And efficient?
  • Is there any other way of doing it?
public class ExtendedViewModel : StandardViewModel
{
    public string Email { get; set; }

    public ExtendedViewModel(StandardViewModel base)
    {
        this = base
    }
}

Edit

I'm doing this for not only one but several classes. And they do not have only two properties so I'm trying to avoid copying the values one by one.

Octan
  • 348
  • 4
  • 19
  • 4
    You do notice that `this = base` won't compile? You have to copy the properties. – Clemens Aug 05 '21 at 17:54
  • I don't have access to my PC so I was writing code out of my head. I do need to copy the properties one by one? Is there any other method? – Octan Aug 05 '21 at 18:02
  • Could I use an static method to construct the new class without having to copy each variable? I don't know if something like that would compile: `public static ExtendedViewModel MyConstructor(StandardViewModel base) {ExtendedViewMode vm = new(); vm = base; return vm;}` – Octan Aug 05 '21 at 18:11
  • 1
    Copying the properties might be the simplest solution here. If your base class has a [copy constructor](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-write-a-copy-constructor) then you can use that to copy. You can also look at [`record`](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/records) if it suits your needs. – another.anon.coward Aug 05 '21 at 18:45

2 Answers2

2

Finally I'll avoid using inheritance and I'll create a new class, expose the base class and subscribe to INotifyPropertyChanged as described here and here.

This way I'll be able to have properties that depend on the Base ViewModel updated as done with FullName below.

The resulting ViewModel will look like:

public class ExtendedViewModel
{
    public StandardViewModel Base { get; set; }
    public string Email { get; set; }

    public string FullName {
        get => Base.FirstName + Base.LastName;
    }

    public ExtendedViewModel(StandardViewModel base)
    {
        Base = base
        Base.PropertyChanged += BaseChanged
    }

    private void BaseChanged(object sender, PropertyChangedEventArgs e)
    {
        // Here check if FirstName or LastName changed and
        RaisePropertyChanged("FullName");
    }
}

In the view I will bind directly to Email or to Base.FirstName.

Octan
  • 348
  • 4
  • 19
  • How are you keeping the collections of `StandardViewModel` & `ExtendedViewModel` in sync? – Alex Hope O'Connor Aug 06 '21 at 04:19
  • I'm not sure if I got your question right. Now that `ExtendedViewModel` does **not** subclass `StandardViewModel` I don't have any duplicate properties to keep in sync. Each view model takes care of its own properties. And with the `BaseChanged` notification I can detect those changes in `StandardViewModel` that affect the `ExtendedViewModel`. – Octan Aug 06 '21 at 05:06
  • So when you bind the `ItemsSource` on the two seperate data grids do they bind to the same property/collection? From the context of the question I assumed you had a collection of each type now, with the `ExtendedViewModel` collection depending on the elements of the `StandardViewModel` collection. Inheritance or composition work as well as the decorator pattern, whichever you pick really depends on what your actually trying to do and how many elements you might be trying to manage. – Alex Hope O'Connor Aug 08 '21 at 03:24
  • Yes. So I have a collection of `StandardViewModel` in the data grid. Then, the selected element is added in the `ExtendedViewModel` to be edited in a form (I don't do direct edit in the DataGrid). Changes done in the `ExtendedViewModel` In the form are being displayed in the DataGrid as expected. – Octan Aug 08 '21 at 13:06
1

In oversimplified terms a decorator owns an instance of the class it decorates and delegates functionality existing in the owned class to that class, while adding new functionality uncoupled from the owned class instance.

public class StandardViewModel
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class ExtendedViewModel : StandardViewModel
{
    private StandardViewModel _standard;
    public ExtendedViewModel (StandardViewModel standard)
    {
         if (standard.GetType() != typeof(StandardViewModel )) {
            throw new ArgumentException ("Expected a non derived standard view model", nameof(standard));
         }
        _standard = standard;
    }
    public string Email { get; set; }
    public override string FirstName { 
        get => _standard.Firstname; 
        set  => _standard.Firstname = value; 
    }
    public override string LastName { 
        get => _standard.LastName; 
        set  => _standard.LastName = value; 
    }

}
lidqy
  • 1,891
  • 1
  • 9
  • 11
  • 2
    I think that most ViewModels are implementing the `INotifyPropertyChanged` interface. How would you implement it in ExtendedViewModel? Especially to also reflect changes when the properties of the decorated class are directly changed. Is there some tricky way? – Steeeve Aug 06 '21 at 01:00
  • Thanks for the example on the decorator. However, as @Steeeve says, I need to subscribe somehow to the `INotifyPropertyChanged` interface. – Octan Aug 06 '21 at 03:56
  • @Steeve although its true most VMs impl INPC, it's not relevant to the question asked. I just used the OP's code to show how one can extend this code following the decoration pattern (or sth alike). I don't spend my spare time as a free online coding service. – lidqy Aug 06 '21 at 14:00