背景和问题
我有一个WPF应用程序,它基本上由一组不同的技能对象组成,其中每个技能都是从一个抽象基类(SkillBase)继承而来的。每个技能都应该有自己的View和ViewModel,以支持编辑特定技能的值。当用户选择列表中的技能并按下"编辑"时,应该在注入技能的情况下实例化相应的Edit-ViewModel。
当我只有Skill-data对象时,如何选择应该实例化的Edit-ViewModel?技能可以通过插件系统添加,所以我事先不知道技能的类型。
我今天的解决方案(可行)是使用属性(EditViewModelForAttribute)来装饰每个Edit-ViewModel,告诉它应该映射到哪种数据类型。这是要走的路还是有更好的方法吗?我甚至走在正确的轨道上吗?
我能想到的其他解决方案是跳过属性并使用Edit-ViewModels的某种命名约定,另一种解决方案是在启动时通过注入服务注册具有Edit-ViewModel类型的Skill类型( EditService.RegisterEditViewModel(typeof(WalkSkill), typeof(EditWalkSkillViewModel));
)
这是我的代码:
"数据层"
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;
}
}
要为特定技能找到正确的ViewModel,我有一个扩展方法来搜索app域中具有EditViewModelForAttribute且具有特定技能类型的类型:
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);
}
这就是这样称呼的:
var editViewModel = selectedSkill.GetEditSkillViewModel();
答案 0 :(得分:1)
您使用的当前解决方案似乎很好,只要您每个技能只有一个ViewModel。
但是有一个建议是,使用FirstOrDefault
代替SingleOrDefault
并将结果缓存到某处。搜索所有类型可能非常耗时。
答案 1 :(得分:1)
就软件开发而言,您似乎走在了正确的轨道上。您当前的解决方案没有任何问题。如果你使用像MEF这样的框架,这是经典的方法。
那说,如果你要继续;考虑切换到MEF或任何其他框架,而不是编写自己的属性/导入函数/其他术语。
// Define other methods and classes here
public abstract class EditSkillViewModel<T> where T : SkillBase
{
public T Skill { get; protected set; }
protected EditSkillViewModel(T skill){
Skill = skill;
}
}
public static class EditSkillViewModelManager
{
private static IDictionary<Type, Type> _types;
public static EditSkillViewModel<T> CreateEditSkillViewModel<T>(T skill)
where T : SkillBase
{
return (EditSkillViewModel<T>)Activator.CreateInstance(_types[typeof(T)], skill);
}
static EditSkillViewModelManager()
{
var editSkillViewModelTypes = from x in Assembly.GetAssembly(typeof(SkillBase)).GetTypes()
let y = x.BaseType
where !x.IsAbstract &&
!x.IsInterface &&
y != null &&
y.IsGenericType &&
y.GetGenericTypeDefinition() == typeof(EditSkillViewModel<>)
select x;
_types = editSkillViewModelTypes
.Select(x =>
new {
To = x, // EditWalkSkillViewModel
From = x.BaseType.GetGenericArguments()[0] // WalkSkill
})
.ToList()
.ToDictionary(x => x.From, x => x.To);
_types.Dump();
}
}
public class EditWalkSkillViewModel : EditSkillViewModel<WalkSkill>
{
public EditWalkSkillViewModel(WalkSkill skill) : base(skill)
{
}
}
public class EditRunSkillViewModel : EditSkillViewModel<RunSkill>
{
public EditRunSkillViewModel(RunSkill skill): base(skill)
{
}
}
答案 2 :(得分:1)
我正在解决非常类似的问题,这就是我所做的 - 转换为你的课程:
它基于与您的解决方案相同的想法,但抽象是在其他地方。我这样做了,因为我不得不将UI架构与逻辑分开。如果我想从WPF迁移到其他东西,我唯一要改变的是为所有配置创建视图。只有一个EditViewModel将WPF UI与我的应用程序逻辑/模型连接起来。