0

Forgive the title - I really can't thnk of a more succinct way to put it.

Alright, first of all I am creating a WPF application using C# and MVVM, in Visual Studio 2010.

It's a language program, and my data model has in it some inheritance. A LanguageItem sits at the top. On step down is GeneralVocabItem and then two classes derive from that: Verb and IAdjective. I keep a list of these in XML and I load them all into a list of LanguageItem.

So now to the view model part. I looked up already how to handle view models when the view models represent classes with inheritance. I always thought it looked tricky, but I've managed to avoid it thus far. The suggestion I read here, on Stackoverflow was to use generics and an abstract base class. So this is what I tried to do.

So the base ViewModel is as follows:

public abstract class LanguageItemViewModelBase<TModel> : ObservableObject
{
    private readonly TModel _dataObject;
    protected TModel DataObject { get { return _dataObject; } }

    public LanguageItemViewModelBase(TModel dataObject)
    {
        _dataObject = dataObject;
    }

}

Then I had view models for the classes I wanted. The view model for LanguageItem looks like this:

class LanguageItemViewModel : LanguageItemViewModelBase<LanguageItem>
{
    public LanguageItemViewModel(LanguageItem item) : base(item)
    {

    }

    public int ItemID
    {
        ...
    }

    public int ItemType
    {
        ...
    }
}

and the view model for Verb looks like this:

class VerbViewModel : LanguageItemViewModelBase<Verb>
{
    public VerbViewModel(Verb item) : base(item)
    {

    }

    public int ItemID
    {
        ...
    }

    public int ItemType
    {
        ...
    }

}

Now, what I wanted was a list which could hold any of these view models within it.

The List I declared as follows:

private ObservableCollection<LanguageItemViewModelBase<LanguageItem>> _languageItemVMs = new ...

Now this here might be where my mistake is. Can I have a list of an abstract class like this? And am I limiting myself by assigning the type, or is it okay because all types (for the generic type) derive from LanguageItem anyway? So this part I don't know. Honestly, the territory is already getting a little murky.

Anyhow, when I load a LanguageItem from XML, there is an event handler for my list of view models. It looks like this:

Database.Instance.RegisterForLanguageItemAdded(
            (Object o) =>
            {
                // o is some kind of LanguageItem
                if (o != null)
                {
                    LanguageItem l = (o as LanguageItem);

                    LanguageItemViewModel lvm = new LanguageItemViewModel(l);

                    LanguageItemVMs.Add(lvm);

                }
            });

Now, this works okay kind of. I created a ListBox to test this list and all the items appeared, showing the properties. However, at that stage the view models had all the same exposed values (ItemID and ItemType). As a test, I changed VerbViewModel so it exposed an additional property that was not exposed in LanguageItemViewModel. It did not appear. Perhaps, I thought, it is because I am specifically adding a LanguageItemViewModel to my view model list in the event handler above. So in order to test this, I also added a random VerbViewModel to the list in the event handler.

That looked as follows:

Verb v = new Verb();
                    v.ItemID = 100;
                    v.ItemType = Static_Enums.StaticLanguageItemTypesEnums.TYPE_VERB;

                    VerbViewModel vvm = new VerbViewModel(v);

                    LanguageItemVMs.Add(vvm);

I thought if I specified the type, it might work out.

Unfortunately I get a compile error telling me that 'argument 1 cannot be converted from VerbViewModel to LanguageItemViewModelBase<LanguageItem>'.

So honestly at that stage I thought I'd come and ask here. It might be that the entire organisation is wrong and so worrying about this end part is pointless since it can never work. I might be labouring entirely under some false assumptions and misunderstandings that someone with more experience and knowledge could help me with.

Does the view model list need to be a non abstract type? Does the fact that the generic types accepted by the view models inherit from each other cause too many problems? I mean the view models inherit from each other, but the generic type does too. Honestly it's a bit of a headache really when it comes to trying to have them all in one list. Can it be done? Is there a better / simpler / more easy to understand way?

Well, thanks for reading. I hope I can get some useful tips on this one.

  • Your question is nothing to do with viewmodels, or WPF for that matter. It is simply a question of understanding the C# implementation of inheritance and/or generics. You could reduce this question down to be much more simple and concise, and increase the changes of you getting a response. – LordWilmore Jan 10 '18 at 11:29
  • I suppose so, but the situation is rather unusual isn't it? I mean surely the first question someone would ask is 'wait, why are you trying to wrap your classes in other classes'? And then I'd answer saying it was for WPF and then I'm sure someone would ask why I didn't just say that in the first place. Also my reduction of the question may introduce further errors. – TheFaithfulLearner Jan 10 '18 at 11:43
  • your requirement to "wrap" classes inside each other is not unusual, and is certainly not specific, or in fact pertinent to WPF. It's a general question regarding the storage of polymorphic generic classes. It's a valid question, and a good question, but the information about WPF and viewmodels is unnecessary, and therefore likely to mean that you will get fewer responses. I'd open a new C# console project, create a couple of simple classes that match this pattern, and see if you can replicate the problem, and ask it using that example - it ought to be pretty simple to describe – LordWilmore Jan 10 '18 at 11:45
  • You can always `private ObservableCollection`, there's nothing wrong with that. You can always `_languageItemVMs.OfType().Where(x => ...` to get individual types out. It's not glorious, but it's okay for exposing these objects for binding. You might also look into creating an interface for the abstract type and its implementations if that makes you less mad and feel less blame... No guarantee it'll be useful, however. –  Jan 10 '18 at 20:34

0 Answers0