RBT
RBT

Reputation: 561

MVVM with a tree of models

I have an application with a tree of model objects which constitute a kind of schema. This might look like:

ElementSet
....ElementGroup
........ScalarElement
........BoolElement
....ElementGroup
........MatrixElement
........VectorElement

This tree is much more complex, but you get the idea. I will be displaying this tree in a WPF TreeView, allowing the user to not only select a particular node (master-detail style) but also add, remove and rearrange nodes.

My intention is to encapsulate each node in the tree in a ViewModel, where I will add commands, tooltip info and other ViewModel related fluff.

My problem comes in that I have a tree of models, and would really need a tree of ViewModels to go with it. I'm struggling to think of an elegant solution to this. How to I construct this? Keep them in sync? etc

One idea I'm toying with is binding the View directly to the graph of model objects, but using a ValueConverter to convert the Model to a ViewModel on each node. Each Model has a unique ID, so a having the converter maintain a queryable cache of ViewModels to return to the view wouldn't be out of the question.

What other effective strategies exist for creating view models for collections/trees of models?

Upvotes: 2

Views: 958

Answers (2)

RBT
RBT

Reputation: 561

I ended up using a ValueConverter with a ViewModel cache. It actually works quite nicely. I set up my TreeView like this:

<TreeView ItemsSource="{Binding TreeRoot}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <ContentPresenter Content="{Binding Converter={StaticResource ModelToViewModelConverter}}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

...where TreeRoot is the root of a tree of model objects, each with a property "Children". And created my converter like this:

public class ModelToViewModelConverter : IValueConverter
{
    private static Dictionary<Guid, ViewModelBase>  _map = new Dictionary<Guid, ViewModelBase>();

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var element = value as ElementBase;

        if (!_map.ContainsKey(element.Id))
        {
            if (element is ElementSet)_map.Add(element.Id, new ElementSetViewModel(value as ElementSet));
            if (element is ElementGroup) _map.Add(element.Id, new ElementGroupViewModel(value as ElementGroup));
            if (element is StringElement) _map.Add(element.Id, new StringElementViewModel(value as StringElement));
            if (element is ScalarElement) _map.Add(element.Id, new ScalarElementViewModel(value as ScalarElement));
        }

        return _map[element.Id];
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        //A change.sdfsdfdsff
        throw new NotImplementedException();
    }
}

This allows me to maintain just one tree of Model objects, but allows me to use ViewModels for the View to bind to.

Upvotes: 0

Gusdor
Gusdor

Reputation: 14334

Depending on the complexity of the data, I recommend a View model for each element and a constructor that will recursively build a tree from your model.

If you simply want to display strings and formatted doubles from your model, you may not even need a view model but it will make your system flexible in the long run.

Upvotes: 1

Related Questions