5

I have a base ViewModel and associated View. I also have multiple derived ViewModels from the base ViewModel, but I'd like to use the base View for display.

Base ViewModel and View:

  • vm: MyCompany.MyApp.Modules.Wizard.ViewModels.WizardViewModel
  • vw: MyCompany.MyApp.Modules.Wizard.Views.WizardView

Derived from WizardViewModel:

  • vm: MyCompany.MyApp.Modules.NewSpec.ViewModels.NewSpecViewModel : WizardViewModel
  • vw: (map to MyCompany.MyApp.Modules.Wizard.Views.WizardView)

  • vm: MyCompany.MyApp.Modules.NewSpec.ViewModels.NewMaterialViewModel : WizardViewModel

  • vw: (map to MyCompany.MyApp.Modules.Wizard.Views.WizardView)

I think this should be possible using the mapping in ViewLocator or ViewModelLocator or NameTransformer, but I haven't figured it out yet.

I am using the Gemini Framework with Caliburn.Micro v1.5.2 (I plan on upgrading to v2 soon).

Here is one of the things I have tried:

public class NewSpecViewModel : WizardViewModel
{
    // ...
    static NewSpecViewModel()
    {
        // Escape the '.' for the regular expression
        string nsSource = typeof(NewSpecViewModel).FullName.Replace(".", @"\.");
        string nsTarget = typeof(WizardViewModel).FullName;
        nsTarget = nsTarget.Replace("WizardViewModel", "Wizard");
        // nsSource = "MyCompany\\.MyApp\\.Modules\\.NewSpec\\.ViewModels\\.NewSpecViewModel"
        // nsTarget = "MyCompany.MyApp.Modules.Wizard.ViewModels.Wizard"
        ViewLocator.AddTypeMapping(nsSource, null, nsTarget);
    }
    // ...
}

P.S. I know there are existing Wizard frameworks (Extended WPF Toolkit, Avalon Wizard, etc), but I don't want to add another 3rd party assembly and the Extended WPF Toolkit Wizard wasn't working properly.

P.P.S. I also want to use this style of base ViewModel/View mapping elsewhere.

Ryan
  • 7,835
  • 2
  • 29
  • 36

2 Answers2

7

Here's [a link] (https://caliburnmicro.codeplex.com/discussions/398456) to right way to do this.

EDIT: Since codeplex is shutting down, here is the code from the discussion:

var defaultLocator = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
    var viewType = defaultLocator(modelType, displayLocation, context);
    while (viewType == null && modelType != typeof(object))
    {
        modelType = modelType.BaseType;
        viewType = defaultLocator(modelType, displayLocation, context);
    }
    return viewType;
};
Ryan
  • 7,835
  • 2
  • 29
  • 36
Edgars Roze
  • 116
  • 2
  • 4
1

I know it's late... but there is an option to bind the ViewModel to a view directly, and maybe this helps others.

I would also append this binding to the base classes constructor. The following works for me:

public abstract class WizardViewModel {
    protected WizardViewModel() {
        // this --> points the child class
        ViewModelBinder.Bind(this, new WizardView(), null);
    }
}

With this, each child now uses the WizardView (without any additional programming in the child class).

public class NewSpecViewModel : WizardViewModel {}
WPFGermany
  • 1,639
  • 2
  • 12
  • 30
  • 1
    I think this works in some cases, but be careful with it. In this case the ViewModel must be constructed on the UI thread, becaus otherwiese the View can't get initialized. – andreas Aug 15 '18 at 11:15