Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM - Collection with polymorphism

I'm trying to expose two derived types in a ListBox: OutFlight and InFlight both derive from Flight which is an abstract type.

MVVM says that I have to set a ViewModel for the ListBox' bound Model. It's not a big deal for exposig one type, but my listbox contains two derived types, and I'm looking for the best MVVM approach. Does it mean I have to set inherited ViewModels for each of the derived types? I've read somewhere ViewModel inheritance is not recommended at all... I'm sure it is a very common case

like image 379
Giora Ron Genender Avatar asked Mar 24 '23 09:03

Giora Ron Genender


1 Answers

There are several ways. One way is to write a ViewModel for your Flight class and fill a collection with those "FlightViewModel" objects. That ViewModel can contain all objects that inherit from "Flight". If your "InFlight" and "OutFlight" classes not so complex, I would wrap them in one ViewModel (here the "FlightViewModel").

public class FlightViewModel : INotifyPropertyChanged
{
    public Flight Flight { get; set; }

    public int PropertyYouNeedForInFlight { get; set; }
    public string PropertyYouNeedForOutFlight { get; set; }
}

An Other Way is to use a collection of some base ViewModel type as the ListBox ItemsSource. That collection contains some ViewModels of type "InFlightViewModel" and some others of type "OutFlightViewModel". For your ListBox items you could write an ItemTemplateSelector that choose the correct ItemTemplate for the type of the item.

public class MainWindowViewModel
{
    public ObservableCollection<ViewModelBase> Flights { get; set; }

    public MainWindowViewModel()
    {
        Flights = new ObservableCollection<ViewModelBase>();
        Flights.Add(new InFlightViewModel());
        Flights.Add(new OutFlightViewModel());
    }
}

public class FlightTemplateSelector : DataTemplateSelector
{
    public DataTemplate InFlightTemplate { get; set; }
    public DataTemplate OutFlightTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, 
                                                DependencyObject container)
    {
        if(item.GetType() == typeof(InFlight))
            return InFlightTemplate;
        if(item.GetType() == typeof(OutFlight))
            return OutFlightTemplate

        //Throw exception or choose some random layout
        throw new Exception();
    }
 }

<local:FlightTemplateSelector
    x:Key="FlightTemplateSelector">
    <local:FlightTemplateSelector.InFlightTemplate>
       <!-- Define your layout here -->
    </local:FlightTemplateSelector.InFlightTemplate>
       <!-- Define your layout here -->
    <local:FlightTemplateSelector.OutFlightTemplate>
    </local:FlightTemplateSelector.OutFlightTemplate>
</local:FlightTemplateSelector>
like image 166
Andre Avatar answered Apr 02 '23 12:04

Andre