【问题标题】:Prism/MVVM (MEF/WPF): Exposing navigation [Menu's for example] from modulesPrism/MVVM (MEF/WPF):从模块中公开导航 [例如菜单]
【发布时间】:2011-05-09 21:19:01
【问题描述】:

我开始使用 MEF 和 WPF 首次涉足 Prism v4/MVVM 世界。我已经成功构建了一个 shell,并且使用 MEF,我能够发现和初始化模块。但是,我不确定为这些模块公开的视图提供导航的正确方法。

例如,假设其中一个模块公开了三个视图,我想在菜单控件上显示到这些视图的导航。到目前为止,我已经成功地公开了一个基于MenuItem 的视图,并且这个MenuItem 包含子MenuItem 控件,从而提供了一个可以使用的命令层次结构。太好了。

问题是,这感觉不对。我现在在我的模块中声明导航(以及外壳)必须支持使用菜单。如果我想改用ToolBar 甚至Ribbon 怎么办。然后我将不得不更改我的所有模块以公开外壳的相应控件类型。

我环顾四周,在一些网站上提到使用“服务”来提供导航,在模块初始化期间,导航选项被添加到服务中,而该服务又被 shell 用来显示这个以它想要的任何格式导航(ToolBarTreeViewRibbonMenuItem 等) - 但我找不到任何实际这样做的例子。

为了透视所有这些,我最终希望能够从菜单和/或其他导航控件(可能是Ribbon)中选择视图,然后在 TabControl 中按需打开这些视图。我已经能够在模块初始化时在TabControl 中创建视图,现在我需要下一步。

我需要知道的是:什么是公开导航选项的正确方法,而不是坚持由 shell 支持特定控件,如果服务是要走的路,那么如何有人会将其放在 Prism/MVVM 模式中吗?

提前感谢您提供的任何见解。

【问题讨论】:

    标签: wpf navigation prism mef


    【解决方案1】:

    我想你有一个包含通用接口的主模块。 您可以创建一个简单的界面,例如

    public interface IMenuService {
        void AddItem(string name, Action action);
        IEnumerable<MenuItemViewModel> GetItems { get; }
    }
    

    创建 1 个实现和单个实例。

    public class MenuService : IMenuService {
    
        private readonly IList<MenuItemViewModel> items = new List<MenuItemViewModel>();
    
        void AddItem(string name, Action action) {
            items.Add(new MenuItemViewModel {
                Name = name,
                Action = action
            });
        }
    
        IEnumerable<MenuItemViewModel> GetItems {
            get { return list.AsEnumerable(); }
        }
    }
    

    在您的模块中,使用 MEF 解析此实例并调用 AddItem() 来注册您的视图。 Action 属性是激活视图或执行其他任何操作的简单委托。

    然后在您的 shell 或任何视图中,您只需要调用 GetItems 属性来填充您的菜单。

    【讨论】:

    • 我有点喜欢这个,因为它是一个完全通用的选项,让外壳决定如何显示项目。也就是说,我对这个主题有进一步的想法,实际上已经走了另一条路线,我将作为单独的答案记录下来。谢谢。
    • 不要忘记您可以将MenuItemViewModel 安装到 MenuItems(可检查或具有子项)。您将不得不制作一个更具体的界面。但这是一个例子。玩得开心。
    【解决方案2】:

    在考虑了更多之后,我得出了以下结论,我觉得这会影响我处理这个问题的方式......

    无论如何,模块都需要部分了解 shell 布局 - 也就是说,shell 公开了许多区域,并且模块需要了解这些区域(按名称以及预期显示的内容)以便在请求功能时正确填充它们(通过在区域内注册视图或作为对用户操作的反应)。

    因此,模块需要设计为与 shell 交互以将内容放置到命名区域中,因此,我认为模块不应该公开 shell 支持的任何类型的导航是没有理由的。

    因此,我的模块(当前)公开了一个“RibbonView”(一个 RibbonTab),其中包含必要的图标、按钮和命令等,以公开模块的功能。每个“RibbonView”都注册到外壳的“RibbonRegion”,以及排序提示,然后在外壳中呈现。

    如果将来我选择更新我的 shell 以使用最新的+最好的导航控件(无论可能在 x 年的时间内),那么我只需要更新每个模块以公开与新导航集成的必要项目,因为我正在加载到一个新的 shell,然后我可以相应地更新我的视图注册。

    我只是希望我这样做不会违反复合应用程序的任何原则,但这表示我还没有找到一种可以在没有一些“解释”的情况下在实际场景中实际实现的模式。

    我很想知道是否有人对此有任何意见。

    【讨论】:

      【解决方案3】:

      我也遇到过同样的情况,我认为解决方案在于区分接口和实现。例如,您可以在执行给定功能的模块中设计视图。这就是它所做的一切。一旦您在特定上下文中使用或使用它,您就已经跨入了实现。现在,理想情况下,视图不知道它是如何实现的,当然也不知道 Shell 中的命名区域。因此,在一个模块中将视图放入区域中是不允许的。

      为了解决这个问题,我选择将此责任委托给第三方组件 LayoutManager。 LayoutManager 位于 Shell 和 Module 之间,并定义“去哪里”。它是一个具体的实现,并且真正定义了实现。 Shell 和 Module 视图都保持通用。

      看看:http://rgramann.blogspot.com/2009/08/layout-manager-for-prism-v2.html

      这可能会给你一些关于这个问题的想法。

      希望对你有帮助。

      【讨论】:

      • 我完全同意,通过了解 shell 区域,我的模块与特定的实现相关联,但到目前为止,我一直在努力寻找如何规避这一点的想法。尽管您的文章没有直接解决我的情况(您只有一个区域,并且您完全了解正在加载的视图以便将它们放入您的配置中),但它给了我一些关于如何绕过的想法问题。谢谢。
      【解决方案4】:

      article 使用抽象 (IMenuItem) 来表示您的菜单选择的 ViewModel。您如何实际呈现这些导入的对象实际上取决于主机应用程序。该示例使用 WPF 菜单,但您当然可以以任何您想要的方式呈现它,因为 IMenuItem 已经足够抽象了。

      如果您将IMenuItem 更改为INavigationItem,您将得到您想要的。

      在那篇文章中,当特定导航项被通知它已“运行”时,它通常会为文档或“pad”实例化一个 ViewModel,并将其传递给 ILayoutManager 服务。它具有可插拔的架构,因此您可以将 LayoutManager 服务换成不同的布局引擎(默认是 AvalonDock 的包装器)。

      【讨论】:

        猜你喜欢
        • 2011-03-24
        • 2011-04-16
        • 2012-01-17
        • 2011-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-29
        • 2012-07-17
        相关资源
        最近更新 更多