wpfmvvmcaliburn.microavalondockstylet

Adding Views/ViewModels to an ObservableCollection when using ViewModel-First MVVM Stylet framework


When using a ViewModel first MVVM pattern, how can you add your ViewModels to an ObservableCollection so they can be bound into another View and appear as a view?

I’m using AvalonDock, and I’m struggling to bind the LayoutDocument panes in AvalonDock to my View.

I’m using Stylet as my MVVM framework, but I’ve also tagged it as Caliburn-Micro as I assume my problem is relevant to any ViewModel-First implementation of MVVM.

My test ObservableCollection is populated as follows:

        Documents = new ObservableCollection<LayoutDocument>();

        LayoutDocument layout1 = new LayoutDocument();
        layout1.Title = "New Document";
        UserControl1 control = new UserControl1();
        layout1.Content = control;

        LayoutDocument layout2 = new LayoutDocument();
        layout2.Title = "New Document 2";
        HomeViewModel control2 = HomeViewModel;
        layout2.Content = control2;

        LayoutDocument layout3 = new LayoutDocument();
        layout3.Title = "New Document 3";
        HomeView control3 = HomeView;
        layout3.Content = control3;


        Documents.Add(layout1);
        Documents.Add(layout2);
        Documents.Add(layout3);

UserControl1 is Initialized via InitializeComponent() in code behind and works perfectly. See image below (first tab).

HomeViewModel which is how I would like to attach them inline with Stylet requirements has been brought through the constructor and AvalonDock doesn’t seem to recognized it as a ViewModel. It just displays the URL to the ViewModel.

HomeView (which is a View) has had the code behind deleted, and I would have thought it would be initialized by Stylet. The object has been injected through the constructor. In AvalonDock it’s behaving as an uninitialized view so just shows nothing.

Here is the Constructor:

public DockingViewModel(HomeViewModel homeViewModel, HomeView homeView)
    {
        HomeViewModel = homeViewModel;
        HomeView = homeView;
        LoadInitialTabs();
    }

I don’t believe the issue is with AvalonDock, but here is how the views are bound.

<DockingManager.LayoutItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding Content , FallbackValue=#ERROR Content.title#}"></ContentControl>
    </DataTemplate>
</DockingManager.LayoutItemTemplate>

Note in the image below of the different results when LayoutDocuments are created in different ways.

How can I get this working in a ViewModel first approach, i.e. using Stylet?

enter image description here


Solution

  • I found the solution. As I am using Stylet I needed to make the following changes in my View.

    First, add the reference for Stylet.

    xmlns:s="https://github.com/canton7/Stylet"
    

    Second: Change the DataContent binding from:

    <ContentControl Content="{Binding Content , FallbackValue=#ERROR Content.title#}"></ContentControl>
    

    To:

    <ContentControl s:View.Model="{Binding Content , FallbackValue=#ERROR Content.title#}"></ContentControl>
    

    With Stylet, the s:View.Model bit automatically finds the View from the ViewModel and Initializes it. Now, I can hold nothing but ViewModels in my ObservableCollection and it works fine/