0

I have several DataGrid (actually an UserControl based on a DataGrid with filtering dialog, etc.) in a WPF project which show ObservableCollection<VisualXModel> entities where VisualXModel inherits from XModel where X stands for Field, Item, etc. The XModel are in a separate non-UI related library which includes the data access layer. Most of the properties in the XModel are virtual and overriden in the VisualXModel in order to deal with validation.

I also implemented the INotifyPropertyChanged and INotifyDataErrorInfo interfaces, but realise to lead to much duplicated codes.

I wonder if it shouldn't be possible to use a base class VisualModel<XModel> which implements the interfaces as well as other common commands such as CopyCellCommand, EditCommand, RemoveCommand.

But is there a way I can access the generic instance (type parameter) in the "overriden" properties which previously corresponded to the base class? The root part of the inheritance still remain the type parameter.


public class XModel
{
    public int Id { get; set; }
    public virtual string Name { get; set; } // Name is made editable from the UI.
}

Currently :

public class VisualXModel : XModel, INotifyPropertyChanged, INotifyDataErrorInfo
{
    public override string Name
    {
        get => base.Name;
        set
        {
            base.Name = value;
            
            // Validation
            // ..
            
            OnPropertyChange(nameof(Name));
        }
    }
    
    // Miscellaneous commands, such as Copy-to-clipboard
    // ..

    // INotifyPropertyChanged, INotifyDataErrorInfo implementations.. which is quite long to duplicate
    // ..
}

With some refactoring

public class VisualModel<T> : INotifyPropertyChanged, INotifyDataErrorInfo
{
    // Miscellaneous commands, such as Copy-to-clipboard
    // ..

    // INotifyPropertyChanged, INotifyDataErrorInfo implementations
    // ..
}

public class VisualXModel : VisualModel<XModel>
{
    public override string Name
    {
        get => How to access / get the Name property of the XModel instance?
        set
        {
            How to access / set the Name property of the XModel instance?

            // Validation
            // ..

            OnPropertyChange(nameof(Name));
        }
    }
}
3
  • 4
    Showing current implementation (in form of minimal reproducible example) would be more productive to start. Have you consideret a code-review? Missing UI technology tags, especially mvvm are alarming. Commented Sep 16 at 15:49
  • 1
    Agree with Sinatr - a concrete example (minimal, ideally) with a concrete question would be much, much more feasible to answer than the very generalized question here. Commented Sep 16 at 15:50
  • Side note: "property of the XModel instance" - VisualModel<XModel> does not in any way define "instance of XModel"... If you think Generic<T> somehow creating instance of T you need to reread about generics. Commented Sep 16 at 16:57

1 Answer 1

1

In my opinion your problem starts that you try to violate Single Responsibility Principle and try to define class that would act as DTO and as view model.

I would take different approach here - separate DTO, which can be even the model class itself and define view model that would implement interfaces INotifyPropertyChanged and INotifyDataErrorInfo.

View model then would take the domain object in it's constructor and "prepare the data" for display.

Roughly the code should look like

public class VisualXModel : INotifyPropertyChanged, INotifyDataErrorInf
{
    public XViewModel(XModel model)
    {
        //...prepare data for display 
    }

    // ... interfaces implementation
}

By the way, by passing XModel through constructor, we are preferring composition over inhertiance here.

Then you have opened options for centralizing code. For instance define common base class for view models:

public class BaseVisual : INotifyPropertyChanged, INotifyDataErrorInf
{
    // ... interfaces implementation
}

// Now view models can be simplified
public class VisualXModel : BaseVisual
{
    public XViewModel(XModel model)
    {
        //...prepare data for display 
    }
}

EDIT You can make use of default interface methods, which would allow you to define IBaseVisual:

public interface IBaseVisual: INotifyPropertyChanged, INotifyDataErrorInf
{
    // ... interfaces implementation with default interface methods
}

public class VisualXModel : XModel, IBaseVisual
{
    // ... you have XModel props and IBaseVisual implementations
}

EDIT Referring to your code sample

public class VisualXModel : VisualModel<XModel>
{
    public override string Name
    {
        get => How to access / get the Name property of the XModel instance?
        set
        {
            How to access / set the Name property of the XModel instance?

            // Validation
            // ..

            OnPropertyChange(nameof(Name));
        }
    }
}

You could put generic parameter contraint. If you have some base class for all models, that for instance defines Name property, then you could write

public class VisualTModel : VisualModel<TModel> where TModel : BaseModel
{
    public override string Name
    {
        get => // Here you will be able to access Name property of TModel object
        set
        {
            // Again you can use object of TModel class as long as it exposes setter property.

            // Validation
            // ..

            OnPropertyChange(nameof(Name));
        }
    }
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for your explanations. I actually see the view model as an extension of the model. Perhaps, it a bad design ptactice which honestly I ignore. I'm sure that passing the model in the constructor will work but I remember now that I also added a few attributes to the model properties which I also want to resued in the view model... Hopefully, they will implement multi-inheritance in C# 11!
Multiinheritance usually suffers from Diamond Problem and i dont think so this is coming to C# ever, or at least anytime soon
Yes, I'm also afraid it won't be available soon. I read a book about C++ a couple of years ago but don't really remember what the issues were. P.S. I meant .NET 11, but not C# 11 in my previous reply...
By the way, I've just realised that the type parameter has nothing to do with an actual instance. I don't really like using the constructor to pass the object instance and am afraid that I will have to be pleased with the code duplication... Perhaps there may be some ways to shorten the code with weaving or 3rd-party libs, but I don't really know much.
I have added yet another option for you - default interface methods
Alright, I didn't really think about hiding interfaces implementation inside another interface. Othewise, I don't actually think using view model constructor with model will brink additional complexities. I use Automapper in the app and it is quite straightforward with a mapping such as cfg.CreateMap<XModel, VisualXModel>().ConstructUsing(obj => new VisualXModel(obj))
public interface IVisualMode : INotifyPropertyChanged, INotifyDataErrorInfo { string Name { get; set; } } public class VisualModel<XModel> : IVisualMode where XModel : VisualModel<XModel> { public virtual string Name { get; set; } // Miscellaneous commands, such as Copy-to-clipboard // .. // INotifyPropertyChanged, INotifyDataErrorInfo implementations // .. } public class VisualXModel(string name) : VisualModel<VisualXModel> { public override string Name { get; set; } = name; }
which allows this public override string ToString() { return $"{nameof(VisualXModel)} [ {((IVisualMode)this).Name } ]"; } and allows access to INotifyPropertyChanged and INotifyDataErrorInfo via IVisualMode for model specific implementations

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.