Background and Problem
I have a WPF application which basically consist of a collection of different Skill-objects where each skill is inherited from an abstract base class (SkillBase). Each Skill should have its own View and ViewModel to support editing the values of the specific Skill. When the user selects a Skill in a List and presses "Edit", the corresponding Edit-ViewModel should be instantiated with the Skill injected into it.
How do I choose which Edit-ViewModel that should be instantiated when I only have a Skill-data object? Skills can be added through a plugin system so I don't know the type of the skills beforehand.
My solution today (which works) is to decorate each Edit-ViewModel with an attribute (EditViewModelForAttribute) telling what datatype it should map to. Is this the way to go or is there any better ways of doing this? Am I even on the right track?
Other solutions that I can think of is to skip the attribute and use some kind of naming convention of Edit-ViewModels and another solution would be, at startup, register a Skill type with a Edit-ViewModel type through an injected service (EditService.RegisterEditViewModel(typeof(WalkSkill), typeof(EditWalkSkillViewModel));)
This is my code:
"Data Layer"
public abstract class SkillBase
{
// Common properties for all skills
}
public class WalkSkill : SkillBase
{
// Some properties
}
public class RunSkill : SkillBase
{
// Some properties
}
"ViewModel Layer"
public abstract class EditSkillViewModel<T> : ViewModelBase where T : Skill
{
public abstract T Skill { get; protected set; }
}
[EditViewModelFor(typeof(WalkSkill))] // Attribute telling that this viewmodel should be instantiated when we want to edit a WalkSkill object
public class EditWalkSkillViewModel : EditSkillViewModel<WalkSkill>
{
public override WalkSkill Skill { get; protected set; }
public EditWalkSkillViewModel(WalkSkill skill)
{
Skill = skill;
}
}
[EditViewModelFor(typeof(RunSkill))] // Attribute telling that this viewmodel should be instantiated when we want to edit a RunSkill object
public class EditRunSkillViewModel : EditSkillViewModel<RunSkill>
{
public override RunSkill Skill { get; protected set; }
public EditRunSkillViewModel(RunSkill skill)
{
Skill = skill;
}
}
To locate the correct ViewModel for a specific Skill i have an extension method to search for types in the app domain that has the EditViewModelForAttribute with the specific Skill type:
public static ViewModelBase GetEditSkillViewModel(this Skill skill)
{
var viewModelType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where
type.IsDefined(typeof(EditViewModelForAttribute)) &&
type.GetCustomAttribute<EditViewModelForAttribute>().SkillType == skill.GetType()
select type).SingleOrDefault();
return viewModelType == null ? null : (ViewModelBase)Activator.CreateInstance(viewModelType, skill);
}
Which is called like this:
var editViewModel = selectedSkill.GetEditSkillViewModel();
The current solution you are using seems fine by me, as long as you only have one ViewModel for each Skill.
One suggestion though, use FirstOrDefault instead of SingleOrDefault and cache the result somewhere. Searching through all the types can be time consuming.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With