Multiple Views Same ViewModel

Topics: Getting Started
Sep 22, 2011 at 5:54 PM
Edited Sep 22, 2011 at 7:54 PM

I am new to Caliburn Micro and am trying to understand how to use View.Context to show different Views for same view model. In my WPF application, I have two views-StewPlanGenInfoView and StewPlanDetailsView that both are bound to StewPlanViewModel. Before I activate a viewmodel from shellviewmodel, I set viewmodel's actiontype property (which is the what view's context is bound to). However in my ViewLocator, I am always getting a null context and so I always get an error saying cannot find view for StewPlanViewModel.  I also implemented the custom view attribute as suggested in post  

Any help is highly appreciated.

Here is my code. 




  <ContentControl x:Name="ActiveItem"                          


cal:View.Context="{Binding ActionType, Mode=TwoWay}"                             







        public ShellViewModel(DashboardViewModel dashboardViewModel,IEventAggregator events)

            Dashboard = dashboardViewModel;
            Events = events;


 public void ShowDashboard()

        public void CreatePlan()
            CurrentStewardshipViewModel = ObjectFactory.GetInstance();
            CurrentStewardshipViewModel.ActionType = ActionType.StewPlanDetails;
            CurrentViewModel = CurrentStewardshipViewModel;




[View(typeof(StewPlanGenInfoView), Context = "PlanGenInfo")]   

[View(typeof(StewPlanDetailsView), Context = "PlanDetails")]

 public class StewPlanViewModel : AccomplishmentViewModel
        private ActionType _actionType;

        public StewPlanViewModel(StewardshipPlanDTO currentPlan)
            : base(currentPlan)

        public ActionType ActionType
            get { return _actionType; }
                _actionType = value;
                NotifyOfPropertyChange(() => ActionType);


//My custom ViewLocator in boostrapper
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
                var customAttributes = modelType.GetCustomAttributes(typeof(ViewAttribute), false);
                var attribute = customAttributes.OfType().Where(x => x.Context == context).FirstOrDefault();
                if (attribute == null && context!=null)
                    attribute = customAttributes.OfType().Where(x => x.Context.ToString() == context.ToString()).FirstOrDefault();
                //return attribute != null ? attribute.ViewType : null;
                return attribute != null ? attribute.ViewType : baseLocate(modelType, displayLocation, context);

Sep 23, 2011 at 8:55 PM

The cal:View.Context attached property in the hosting ContentControl (inside ShellView) won't get any value, since the binding

cal:View.Context="{Binding ActionType, Mode=TwoWay}"        

refers to a (unexisting) "ActionType" property of the ShellViewModel (which happens to be the DataContext in that point).
The child VM is set as DataContext on the injected view only, not on the hosting ContentControl.

cal:View.Context, indeed, is mostly intended to allow the display of the same VM in two or more different areas (with different visual representation).
As a consequence, it doesn't fit your scenario, where you have to choose the correct view depending on a property on the child VM and inject it in a single hosting ContentControl.

You should be able to obtain the expected result tweaking ViewLocator.LocateTypeForModel delegate, which works on the actual instance of the VM: inspecting the instance you can easily determine the correct view based on some conventions or on the [View] attribute you already added.

Sep 26, 2011 at 8:53 PM

Yeah thanks! That was the mistake. I added the ActionType on ShellViewModel and got it working!



Sep 26, 2011 at 9:30 PM

Well, I avoided to suggest you moving ActionType to ShellViewModel because it seemed not very elegant to me.
Doing so, you have to set ActionType explicitly whenever you change the current VM; also, in case of a Shell with multiple items, you have to figure out the proper Context every time an inactive view is indireclty activated (due, for example, to the closing of a previously active VM).

However, if it fits your scenario, your solution is way simpler.