Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf 我可以实现自己的视图解析服务并让RequestNavigate使用它吗?_Wpf_Mvvm_Prism_Prism 4 - Fatal编程技术网

Wpf 我可以实现自己的视图解析服务并让RequestNavigate使用它吗?

Wpf 我可以实现自己的视图解析服务并让RequestNavigate使用它吗?,wpf,mvvm,prism,prism-4,Wpf,Mvvm,Prism,Prism 4,我对Prism还比较陌生,目前正在使用Prism作为概念验证项目重新编写一个现有的应用程序 该应用程序使用MVVM和ViewModel-first方法:我们的ViewModel由容器解析,而IViewResolver服务会确定它应该连接到哪个视图(使用名称约定等) 目前的代码(向选项卡控件添加视图)如下所示: var vm = (get ViewModel from somewhere) IRegion reg = _regionManager.Regions["MainRegion"]; va

我对Prism还比较陌生,目前正在使用Prism作为概念验证项目重新编写一个现有的应用程序

该应用程序使用MVVM和ViewModel-first方法:我们的ViewModel由容器解析,而
IViewResolver
服务会确定它应该连接到哪个视图(使用名称约定等)

目前的代码(向选项卡控件添加视图)如下所示:

var vm = (get ViewModel from somewhere)
IRegion reg = _regionManager.Regions["MainRegion"];
var vw = _viewResolver.FromViewModel(vm); // Spins up a view and sets its DataContext
reg.Add(vw);
reg.Activate(vw);
_regionManager.RequestNavigate(
    "MainRegion", 
    new Uri("NameOfMyViewModel", UriKind.Relative)
);
public class ViewModelContentLoader : RegionNavigationContentLoader
{
    private readonly IServiceLocator serviceLocator;

    public ViewModelContentLoader(IServiceLocator serviceLocator)
        : base(serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    // THIS IS CALLED WHEN A NEW VIEW NEEDS TO BE CREATED
    // TO SATISFY A NAVIGATION REQUEST
    protected override object CreateNewRegionItem(string candidateTargetContract)
    {
        // candidateTargetContract is e.g. "NameOfMyViewModel"

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateTargetContract);
        var viewModel = this.serviceLocator.GetInstance(viewModelType);

        // get ref to viewResolver somehow -- perhaps from the container?
        var view = _viewResolver.FromViewModel(vm);

        return view;
    }

    // THIS IS CALLED TO DETERMINE IF THERE IS ANY EXISTING VIEW
    // THAT CAN SATISFY A NAVIGATION REQUEST
    protected override IEnumerable<object> 
    GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
    {
        if (region == null) {
            throw new ArgumentNullException("region");
        }

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateNavigationContract);

        return region.Views.Where(v =>
            ViewHasDataContract((FrameworkElement)v, viewModelType) ||
            string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
            string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
    }

    // USED IN MY IMPLEMENTATION OF GetCandidatesFromRegion
    private static bool 
    ViewHasDataContract(FrameworkElement view, Type viewModelType)
    {
        var dataContextType = view.DataContext.GetType();

        return viewModelType.IsInterface
           ? dataContextType.Implements(viewModelType)
           : dataContextType == viewModelType 
                   || dataContextType.GetAncestors().Any(t => t == viewModelType);
    }

    // USED TO MAP STRINGS OF VIEWMODEL TYPE NAMES TO ACTUAL TYPES
    private Type GetTypeFromName(string typeName)
    {
        // here you need to map the string type to a Type object, e.g.
        // "NameOfMyViewModel" => typeof(NameOfMyViewModel)

        return typeof(NameOfMyViewModel); // hardcoded for simplicity
    }
}
这一切都很好,但是我真的很想使用Prism导航框架来为我做这些事情,这样我就可以做这样的事情:

var vm = (get ViewModel from somewhere)
IRegion reg = _regionManager.Regions["MainRegion"];
var vw = _viewResolver.FromViewModel(vm); // Spins up a view and sets its DataContext
reg.Add(vw);
reg.Activate(vw);
_regionManager.RequestNavigate(
    "MainRegion", 
    new Uri("NameOfMyViewModel", UriKind.Relative)
);
public class ViewModelContentLoader : RegionNavigationContentLoader
{
    private readonly IServiceLocator serviceLocator;

    public ViewModelContentLoader(IServiceLocator serviceLocator)
        : base(serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    // THIS IS CALLED WHEN A NEW VIEW NEEDS TO BE CREATED
    // TO SATISFY A NAVIGATION REQUEST
    protected override object CreateNewRegionItem(string candidateTargetContract)
    {
        // candidateTargetContract is e.g. "NameOfMyViewModel"

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateTargetContract);
        var viewModel = this.serviceLocator.GetInstance(viewModelType);

        // get ref to viewResolver somehow -- perhaps from the container?
        var view = _viewResolver.FromViewModel(vm);

        return view;
    }

    // THIS IS CALLED TO DETERMINE IF THERE IS ANY EXISTING VIEW
    // THAT CAN SATISFY A NAVIGATION REQUEST
    protected override IEnumerable<object> 
    GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
    {
        if (region == null) {
            throw new ArgumentNullException("region");
        }

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateNavigationContract);

        return region.Views.Where(v =>
            ViewHasDataContract((FrameworkElement)v, viewModelType) ||
            string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
            string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
    }

    // USED IN MY IMPLEMENTATION OF GetCandidatesFromRegion
    private static bool 
    ViewHasDataContract(FrameworkElement view, Type viewModelType)
    {
        var dataContextType = view.DataContext.GetType();

        return viewModelType.IsInterface
           ? dataContextType.Implements(viewModelType)
           : dataContextType == viewModelType 
                   || dataContextType.GetAncestors().Any(t => t == viewModelType);
    }

    // USED TO MAP STRINGS OF VIEWMODEL TYPE NAMES TO ACTUAL TYPES
    private Type GetTypeFromName(string typeName)
    {
        // here you need to map the string type to a Type object, e.g.
        // "NameOfMyViewModel" => typeof(NameOfMyViewModel)

        return typeof(NameOfMyViewModel); // hardcoded for simplicity
    }
}
让Prism旋转ViewModel+视图,设置DataContext并将视图插入区域

通过创建引用ViewModel类型的数据模板,我取得了一些成功,例如:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Module01">
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestView />
</DataTemplate>
</ResourceDictionary>

…并在初始化模块时让模块将相关的资源字典添加到应用程序资源中,但这似乎有点垃圾

是否有一种方法可以有效地从Prism接管视图创建,以便在调用
RequestNavigate
时,我可以查看提供的
Uri
,并在此基础上旋转视图/视图模型?RegionManager.RegisterViewWithRegion中有一个重载,它接受一个委托,允许您自己提供一个视图,我想我想要这样的东西

我想我可能需要提供我自己的
IRegionBehaviorFactory
,但我不确定涉及到什么(或者即使我走的是正确的道路!)

感谢您的帮助

--
注:最初发布在

上,以避免对“视图模型优先方法”的混淆: 您更多地使用“控制器方法”,而不是“视图模型优先方法”。“ViewModel优先方法”是,当您将视图注入ViewModel中,但通过第三方组件(控制器)将ViewModel和视图连接起来时,顺便问一下,(我不想说“最佳”,但)最松散耦合的方法是什么

但要回答你的问题: 一个可能的解决方案是为Prism RegionManager编写一个扩展,它完全按照您上面所描述的那样执行:

public静态类regionmanager
{            
公共静态无效添加区域(
此IRegionManager区域管理器,字符串区域)
{
var viewModel=ServiceLocator.Current.GetInstance();
框架元素视图;
//根据您的约定获取视图
if(view==null)抛出新的NullReferenceException(“未找到视图”);
view.DataContext=viewModel;
regionManager.AddToRegion(区域,视图);
regionManager.Regions[区域].激活(视图);
}
}
然后您可以这样调用此方法:

var vm = (get ViewModel from somewhere)
IRegion reg = _regionManager.Regions["MainRegion"];
var vw = _viewResolver.FromViewModel(vm); // Spins up a view and sets its DataContext
reg.Add(vw);
reg.Activate(vw);
_regionManager.RequestNavigate(
    "MainRegion", 
    new Uri("NameOfMyViewModel", UriKind.Relative)
);
public class ViewModelContentLoader : RegionNavigationContentLoader
{
    private readonly IServiceLocator serviceLocator;

    public ViewModelContentLoader(IServiceLocator serviceLocator)
        : base(serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    // THIS IS CALLED WHEN A NEW VIEW NEEDS TO BE CREATED
    // TO SATISFY A NAVIGATION REQUEST
    protected override object CreateNewRegionItem(string candidateTargetContract)
    {
        // candidateTargetContract is e.g. "NameOfMyViewModel"

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateTargetContract);
        var viewModel = this.serviceLocator.GetInstance(viewModelType);

        // get ref to viewResolver somehow -- perhaps from the container?
        var view = _viewResolver.FromViewModel(vm);

        return view;
    }

    // THIS IS CALLED TO DETERMINE IF THERE IS ANY EXISTING VIEW
    // THAT CAN SATISFY A NAVIGATION REQUEST
    protected override IEnumerable<object> 
    GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
    {
        if (region == null) {
            throw new ArgumentNullException("region");
        }

        // Just a suggestion, plug in your own resolution code as you see fit
        var viewModelType = this.GetTypeFromName(candidateNavigationContract);

        return region.Views.Where(v =>
            ViewHasDataContract((FrameworkElement)v, viewModelType) ||
            string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
            string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
    }

    // USED IN MY IMPLEMENTATION OF GetCandidatesFromRegion
    private static bool 
    ViewHasDataContract(FrameworkElement view, Type viewModelType)
    {
        var dataContextType = view.DataContext.GetType();

        return viewModelType.IsInterface
           ? dataContextType.Implements(viewModelType)
           : dataContextType == viewModelType 
                   || dataContextType.GetAncestors().Any(t => t == viewModelType);
    }

    // USED TO MAP STRINGS OF VIEWMODEL TYPE NAMES TO ACTUAL TYPES
    private Type GetTypeFromName(string typeName)
    {
        // here you need to map the string type to a Type object, e.g.
        // "NameOfMyViewModel" => typeof(NameOfMyViewModel)

        return typeof(NameOfMyViewModel); // hardcoded for simplicity
    }
}
regionManager.AddToRegion(“MyRegion”);

你一定能做到。我发现Prism v4确实是可扩展的,只要您知道在哪里插入

在这种情况下,您需要自己的自定义实现

下面是如何在引导程序中进行设置(示例来自我自己的一个项目中的
UnityBootstrapper
的子类):


这不正是我在最初的示例中所做的吗我所追求的是一种覆盖/影响导航api(即RequestNavigate())如何从URI解析视图实例的方法。我对这个问题进行了略微编辑,希望能让它更清楚!那真是太完美了!谢谢Pj