我终于通过设置自定义视图模型解析器在所有添加的程序集目录中搜索视图模型来解决我的问题。
解决方案
首先,我尝试应用默认的棱镜视图模型定位器约定,如果没有找到视图模型,我开始应用我的自定义。
1-我首先从 AggregateCatalog 获取所有程序集。
2-我获取所有从 Prism BindableBase 继承的非抽象导出类型。
3-我应用自定义约定委托来获得预期的视图模型。
在我的例子中,自定义约定是所有具有后缀“ViewModel”的类型,前缀是视图类型名称: 示例:如果视图名称是“UsersView”,则视图模型应该是“UsersViewModel”。如果视图名称是“Users”,则视图模型也应该是“UsersViewModel”。
代码:
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
viewType =>
{
// The default prism view model type resolver as Priority
Type viewModelType = this.GetDefaultViewModelTypeFromViewType(viewType);
if (viewModelType != null)
{
return viewModelType;
}
// IF no view model found by the default prism view model resolver
// Get assembly catalogs
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
// Get all exported types inherit from BindableBase prism class
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() ;
// Get the type where the delegate is applied
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 * 是默认的棱镜视图模型定位器,其代码与Bart 的答案完全相同。我希望这对其他人有帮助。
编辑:
我终于通过创建一个新的自定义 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)
{
// The default prism view model type resolver as Priority
Type targetType = this.GetDefaultViewModelTypeFromViewType(sourceType);
if (targetType != null)
{
return targetType;
}
// Get assembly catalogs
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
// Get all exported types inherit from BindableBase prism class
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();
// Get the type where the delegate is applied
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)
{
// Get assembly catalogs
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
// Get all exported types inherit from BindableBase prism class
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();
// Get the type where the delegate is applied
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)
{
// Get assembly catalogs
var assemblyCatalogs = this.AggregateCatalog.Catalogs.Where(c => c is AssemblyCatalog);
// Get all exported types inherit from BindableBase prism class
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();
// Get the type where the delegate is applied
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,我就会在 Bootstrapper 上创建它的实例:
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);
}
最后,我只需在 Bootstrapper 中配置视图模型定位器,如下所示:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
viewType => this.mvvmTypeLocator.GetViewModelTypeFromViewType(viewType));
}
请注意,方法GetViewTypeFromViewModelType和GetViewTypeFromViewName正在搜索所有实现名为IView的接口的视图。这只是一个空接口,我用来将我的视图与同一程序集中的其他类区分开来。如果有人使用这个 mvvmTypeLocator,那么他必须创建自己的接口并实现 mvvmTypeLocator 应该可以发现的所有视图。