我最终通过设置自定义视图模型解析器来解决我的问题,以在所有添加的程序集目录中搜索视图模型。
解决方案
首先,我尝试应用默认的Prism视图模型定位器约定,如果没有找到视图模型,则开始应用自定义的约定。
1- 我从AggregateCatalog中获取所有程序集。
2- 获取所有非抽象的从Prism BindableBase继承的导出类型。
3- 应用自定义约定委托以获取预期的视图模型。
在我的情况下,自定义约定是所有类型都具有后缀“ViewModel”,前缀是视图类型名称:
例如:
如果视图名称为“UsersView”,则视图模型应为“UsersViewModel”。
如果视图名称为“Users”,则视图模型也应为“UsersViewModel”。
代码:
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
viewType =>
{
Type viewModelType = this.GetDefaultViewModelTypeFromViewType(viewType);
if (viewModelType != null)
{
return viewModelType;
}
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
var bindableBases =
assemblyCatalogs.Select(
c =>
((AssemblyCatalog)c).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(BindableBase)))
.Select(t => t)).SelectMany(b =>
{
var types = b as IList<Type> ?? b.ToList();
return types;
}).Distinct() ;
var customConvention = new Func<Type, bool>(
(Type t) =>
{
const string ViewModelSuffix = "ViewModel";
var isTypeWithViewModelSuffix = t.Name.EndsWith(ViewModelSuffix);
return (isTypeWithViewModelSuffix)
&& ((viewType.Name.EndsWith("View") && viewType.Name + "Model" == t.Name)
|| (viewType.Name + "ViewModel" == t.Name));
});
var resolvedViewModelType = bindableBases.FirstOrDefault(customConvention);
return resolvedViewModelType;
});
方法 * GetDefaultViewModelTypeFromViewType * 是默认的prism视图模型定位器,其代码与Bart's answer中完全相同。
希望这对其他人有所帮助。
编辑:
最终我通过创建一个新的自定义MvvmTypeLocator解决了问题:
public interface IMvvmTypeLocator
{
#region Public Methods and Operators
Type GetViewModelTypeFromViewType(Type viewType);
Type GetViewTypeFromViewModelType(Type viewModelType);
Type GetViewTypeFromViewName(string viewName);
#endregion
}
实现方式:
public class MvvmTypeLocator: IMvvmTypeLocator
{
private AggregateCatalog AggregateCatalog { get; set; }
public MvvmTypeLocator(AggregateCatalog aggregateCatalog)
{
this.AggregateCatalog = aggregateCatalog;
}
public Type GetViewModelTypeFromViewType(Type sourceType)
{
Type targetType = this.GetDefaultViewModelTypeFromViewType(sourceType);
if (targetType != null)
{
return targetType;
}
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
var bindableBases =
assemblyCatalogs.Select(
c =>
((AssemblyCatalog)c).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(BindableBase)))
.Select(t => t)).SelectMany(b =>
{
var types = b as IList<Type> ?? b.ToList();
return types;
}).Distinct();
var customConvention = new Func<Type, bool>(
(Type t) =>
{
const string TargetTypeSuffix = "ViewModel";
var isTypeWithTargetTypeSuffix = t.Name.EndsWith(TargetTypeSuffix);
return (isTypeWithTargetTypeSuffix)
&& ((sourceType.Name.EndsWith("View") && sourceType.Name + "Model" == t.Name)
|| (sourceType.Name + "ViewModel" == t.Name));
});
var resolvedTargetType = bindableBases.FirstOrDefault(customConvention);
return resolvedTargetType;
}
public Type GetViewTypeFromViewModelType(Type sourceType)
{
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
var bindableBases =
assemblyCatalogs.Select(
c =>
((AssemblyCatalog)c).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(IView)))
.Select(t => t)).SelectMany(b =>
{
var types = b as IList<Type> ?? b.ToList();
return types;
}).Distinct();
var customConvention = new Func<Type, bool>(
(Type t) =>
{
const string SourceTypeSuffix = "ViewModel";
var isTypeWithSourceTypeSuffix = t.Name.EndsWith(SourceTypeSuffix);
return (isTypeWithSourceTypeSuffix)
&& ((sourceType.Name.EndsWith("View") && t.Name + "Model" == sourceType.Name)
|| (t.Name + "ViewModel" == sourceType.Name));
});
var resolvedTargetType = bindableBases.FirstOrDefault(customConvention);
return resolvedTargetType;
}
public Type GetViewTypeFromViewName(string viewName)
{
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
var bindableBases =
assemblyCatalogs.Select(
c =>
((AssemblyCatalog)c).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && typeof(IView).IsAssignableFrom(t) && t.Name.StartsWith(viewName))
.Select(t => t)).SelectMany(b =>
{
var types = b as IList<Type> ?? b.ToList();
return types;
}).Distinct();
var customConvention = new Func<Type, bool>(
(Type t) =>
{
return t.Name.EndsWith("View");
});
var resolvedTargetType = bindableBases.FirstOrDefault(customConvention);
return resolvedTargetType;
}
private Type GetDefaultViewModelTypeFromViewType(Type viewType)
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(
CultureInfo.InvariantCulture,
"{0}{1}, {2}",
viewName,
suffix,
viewAssemblyName);
return Type.GetType(viewModelName);
}
}
这个自定义类型定位器使用AggregateCatalog来搜索所有程序集目录中的目标类型。当然,我会在Bootstrapper上配置AggregateCatalog后创建它的实例:
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(LoginViewModel).Assembly));
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(LoginView).Assembly));
this.mvvmTypeLocator = new MvvmTypeLocator(this.AggregateCatalog);
}
在最后,我只需要像下面这样在引导程序中配置视图模型定位器:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
viewType => this.mvvmTypeLocator.GetViewModelTypeFromViewType(viewType));
}
请注意,方法
GetViewTypeFromViewModelType和
GetViewTypeFromViewName正在搜索所有实现名为
IView的接口的视图。 这只是我用来将我的视图与同一程序集中的其他类区分开来的空接口。 如果有人使用此mvvmTypeLocator,则必须创建自己的接口并实现所有应由mvvmTypeLocator发现的视图。