2

我有一个 WinRT 应用程序,它使用 caliburn.micro INavigationService (FrameAdapter) 运行良好。问题是我的应用程序中的所有页面都是完整页面,并且它们重复了很多视图 xaml 标记。我想做的是拥有更多的“MasterPages”类型的体系结构,其中我有一个 shell 视图,它定义了主布局并包含一个 ContentControl,并使用导体模式来切换 ContentControl 的内容。过去,我使用 WPF 和 Silverlight 在这种架构上取得了成功,所以我认为在 WinRT 中是可能的。

问题是导航基础设施(使用 FrameAdapter)和使用 ContentControl 绑定到导体的 ActiveItem 的导体基础设施(如 SimpleNavigation 示例)之间似乎存在脱节。

在指挥场景中,我使用了 ActivateItem:

ActivateItem(new MyViewModel());

但对于 INavigationService,我使用 NavigateToViewModel:

navigationService.NavigteToViewModel<MyViewModel>();

据我所知,两者似乎没有联系。

我的一个想法是创建一个实现 INavigationService 并基本上处理子屏幕的创建和激活的 ConductorNavigationService。虽然看起来可能看起来不是很直接,所以我想我会检查一下在 caliburn.micro 中是否有已经支持的方法来做到这一点。

4

1 回答 1

3

好的,我的理解可能有点紧张,因为我没有使用过 WinRT 或INavigationService但我认为Frame它是 RT 的一部分,并INavigationService为帧提供视图模型分辨率和导航。

我的另一个假设是你的框架有点像你的指挥,当你在框架上调用'Navigate()'时,它只是用新指定的内容替换框架的内容。如果是这种情况,那么 CM 正在为视图模型执行视图优先分辨率。

既然你想走指挥路线,听起来你想放弃 CM 实现,INavigationService只是滚动你自己来处理INavigationService导航方法(例如跳过FramesNavigate()方法)。

快速查看 CM 源代码会发现,所有NavigationService正在做的事情就是处理Navigate帧上的事件,然后进行 VM 解析并设置视图(指挥可能已经这样做了)。您需要做的就是确保您的INavigationService实现只是将指定的视图加载到外壳中,而不是在框架中导航

您可能只需要窃取构造函数代码NavigationService并更改 的实现Navigate(),然后只需调用ActivateItem(x)您的 shell,其中 x 是 VM 的实例。CM 将负责其余的工作(我认为 CM boostrapper 也已经设置了您的根“框架”,因此您不必担心这一点)。

例如

一个实现可能看起来更像这样(请记住,这只是我拼凑起来的东西,可能是赤裸裸的谎言!):

public class NewFrameAdapter : INavigationService
{
    private readonly Frame frame;
    private readonly IConductActiveItem shell;
    private event NavigatingCancelEventHandler ExternalNavigatingHandler = delegate { };

    public NewFrameAdapter(Frame frame)
    {
        this.frame = frame;

        // Might want to tighten this up as it makes assumptions :)
        this.shell = (frame as FrameworkElement).DataContext as IConductActiveItem;
    }

    public bool Navigate(Type pageType)
    {
        // Do guardclose and deactivate stuff here by looking at shell.ActiveItem
        // e.g.
        var guard = shell.ActiveItem as IGuardClose;

        if (guard != null)
        {
            var shouldCancel = false;
            guard.CanClose(result => { shouldCancel = !result; });

            if (shouldCancel)
            {
                e.Cancel = true;
                return;
            }
        }

        // etc

        // Obviously since the guard is probably async (assume it is, if not you are ok to continue!) you'd have to not call this code right 
        // here but I've just stuck it in here as an example

        // edit: looking at the code above (the guard code) it looks like this is all sync so the below code should be fine

        // You might get away with calling shell.ActivateItem(pageType) as I'm not sure
        // if the viewmodel binder in RT would resolve this all for you, but if it doesnt...

        // Init the view and then resolve the VM type
        ViewLocator.InitializeComponent(pageType);

        var viewModel = ViewModelLocator.LocateForView(pageType);

        // Activate the VM in the shell)
        shell.ActivateItem(viewModel);
    }

以自己的方式滚动应该不会太难。这对你有帮助吗?

那么您的 XAML 将非常简单:

<Frame blah blah>
  <SomeStaticContent />
  <ContentControl x:Name="ActiveItem" /> <!-- The dynamic bit... -->
  <SomeMoreStaticContent />
</Frame>

我认为这可能是视图优先和视图模型优先的混合体,因为您的根Frame将使用视图优先,而您的指挥将使用ActivateItem()哪个视图模型,然后在绑定器启动时解析视图,但如果我的假设没问题,它应该可以工作

于 2012-12-05T17:07:16.123 回答