make ViewLocator resolve types ending with ViewModelDesign at designtime

Topics: Conventions
Jun 26, 2012 at 1:21 PM
Edited Jun 26, 2012 at 3:11 PM

For designtime support I derive from a ViewModel class and add desgintime data in this class. The naming convention is that the derived designtime class has the suffix *Design. And use that class for d:DataContext (with d:IsDesgintimeCreatable=true). So for example for MyViewModel I would have a derived type MyViewModelDesign that I then use like this:

d:DataContext="{d:DesignInstance pre:MyViewModelDesign, IsDesignTimeCreatable=True}" Caliburn:Bind.AtDesignTime="True"

Unfortunately the ViewLocator will not be able to locate the Views for these designtime types, since there is no rule for this.

So my namingconvention is  that "ViewModelDesign$" should be replaced with "View" to locate a view. This should be done only at designtime.

How can I achieve the desired effect? I suppose I need to use ViewLocator.AddTypeMapping?  But what are the correct parameters?

 

From a pure regular expression perspective this works:

[Test]
[TestCase("My.Name.Space.MyViewModelDesign", Result = "My.Name.Space.MyView")]
[TestCase("My.Name.MyViewModelDesign.Space.MyViewModelDesign", Result = "My.Name.MyViewModelDesign.Space.MyView")]
[TestCase("My.Name.Space.MyViewModelDesignX", Result = "My.Name.Space.MyViewModelDesignX")]
[TestCase("MyViewModelDesign", Result = "MyView")]
[TestCase("MyViewModelDesignX", Result = "MyViewModelDesignX")]
public string RegexTest(string input)
{
    const string pattern = @"(?<nsbefore>([A-Za-z_]\w*\.)*)ViewModelDesign$";
    return Regex.Replace(input, pattern, @"View");
}

But when I do 

const string pattern = @"(?<nsbefore>([A-Za-z_]\w*\.)*)ViewModelDesign$";
ViewLocator.AddTypeMapping(pattern, null, "View");

It still says it cannot resolve the views for my viewmodels. 

 


Jun 27, 2012 at 8:01 AM
Edited Jun 27, 2012 at 10:39 AM

Turns out to be pretty easy:

/// <summary>
/// Called by the bootstrapper's constructor at design time to start the framework.
/// </summary>
protected override void StartDesignTime()
{
    base.StartDesignTime();
    ViewLocator.NameTransformer.AddRule("ViewModelDesign$", "View");
}

Jun 27, 2012 at 10:43 PM
Edited Jun 27, 2012 at 11:21 PM

Glad you figured it out. :)

But to ensure robustness of your mappings, you should do it like this:

var config = new TypeMappingConfiguration()
{
    ViewModelSuffix = "MyViewModelDesign",
    NameFormat = "{1}{0}",
    ViewSuffixList = new List<string>(new[] { "MyViewDesign"}),
    IncludeViewSuffixInViewModelNames = false
};

ViewLocator.ConfigureTypeMappings(config);
ViewModelLocator.ConfigureTypeMappings(config);

//test it
typename = "Test.ViewModels.MyViewModelDesignCustomer";
result = String.Join(", ", ViewLocator.TransformName(typename, null).ToArray());
//Output: Test.Views.MyViewDesignCustomer, Test.ViewModels.MyViewDesignCustomer

A key override of the configuration for your convention is the "NameFormat" property. Whereas, the out-of-the-box convention is "<entityname><ViewModel suffix>" (e.g. CustomerViewModel), yours is backwards--"<ViewModel prefix><entityname>" (e.g. ViewModelCustomer or MyViewModelDesignCustomer).

 If you need to override the automatic mappings for subnamespaces for your convention (i.e. *.MyViewDesigns.* -> *.MyViewModelDesigns.* and *.MyViewModelDesigns.* -> *.MyViewsDesigns.* instead of the default *.Views.* -> *.ViewModels.*  and *.ViewModels.* -> *.Views.*), then you need to do so by changing the DefaultSubNamespaceForViews and DefaultSubNamespaceForViewModels properties of TypeMappingConfiguration instance.

var config = new TypeMappingConfiguration()
{
    DefaultSubNamespaceForViews = "MyViewDesigns",
    DefaultSubNamespaceForViewModels = "MyViewModelDesigns",
    ViewModelSuffix = "MyViewModelDesign",
    NameFormat = "{1}{0}",
    ViewSuffixList = new List<string>(new[] { "MyViewDesign"}),
    IncludeViewSuffixInViewModelNames = false
};

ViewLocator.ConfigureTypeMappings(config);
ViewModelLocator.ConfigureTypeMappings(config);
typename = "Test.MyViewModelDesigns.SubNS.MyViewModelDesignCustomer";
result = String.Join(", ", ViewLocator.TransformName(typename, null).ToArray());
//Output: Test.MyViewDesigns.SubNS.MyViewDesignCustomer, Test.MyViewModelDesigns.SubNS.MyViewDesignCustomer
Jul 31, 2012 at 11:10 AM
Edited Jul 31, 2012 at 11:12 AM

Thanks for the reply. I will try to use the TypeMappingConfiguration instead. But I think you got my initial question mixed up a little bit. At designtime the viewmodels classes that are to be used by the ViewLocator end with ViewModelDesign instead of (at runtime) ViewModel. That is the only difference.

So I guess all I have to override is:

var config = new TypeMappingConfiguration()
{   
   ViewModelSuffix = "ViewModelDesign",
};