Caliburn Micro - >从多个Views / UserControls / CustomControls编写视图

时间:2013-12-11 06:53:09

标签: c# mef caliburn.micro

如何在CM托管窗口中重用和组合部件?我找到了关于使用两个UserControl绑定到同一个ViewModel的帖子,但是如果我想在同一个窗口中创建多个视图和视图模型,那就没那么多了。 (每个视图的视图模型组成“主视图”)

我的问题的第一部分是如何分解组件以便重复使用?如果我有一个窗口的两个区域,其中一个是数据网格,另一个是带有标签和文本框的详细信息视图,这些区域应该是单独的用户控件,自定义控件还是窗口?理想情况下,每个人都可以独立,因此可以将它们分开并用于其他窗户。

因此,如果它们被分开,我最终会得到2个视图模型和2个视图。现在假设我想创建3个窗口,一个窗口带有第一个视图,第二个带有第二个视图,第三个带有两个视图。如何使用CM为每个视图创建窗口并将每个视图连接到其视图模型?从我看到的例子中,我在窗口中看到了大部分视图和视图模型。

2 个答案:

答案 0 :(得分:3)

我不会以任何方式声称自己是CM的专家,但我已经用一个简单的“基准探险家”取得了合理的成功。它使用单个“shell视图”组成另外两个视图,每个视图都有自己的ViewModel。 shell视图如下所示:

<Window x:Class="NodaTime.Benchmarks.Explorer.Views.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="NodaTime Benchmarks" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
        <ContentControl x:Name="BenchmarkPicker" Grid.Column="0"/>
        <GridSplitter ... />
        <ContentControl x:Name="ResultsGraph" Grid.Column="2"/>
    </Grid>
</Window>

然后ResultsGraphViewBenchmarkPickerView都是这样的:

<UserControl x:Class="NodaTime.Benchmarks.Explorer.Views.ResultsGraphView"
    ... namespaces etc ...>
    <Grid>
        <Grid.RowDefinitions>...</Grid.RowDefinitions>
        <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
        ... controls ...
    </Grid>
</UserControl>

ShellViewModel将其他两个ViewModel公开为属性。然后在构造时自动将它们传递给视图。 (引导程序没有提供任何获取它们的方法。)

现在这不是完全符合您的描述,因为我认为您不能单独使用两个单独的视图作为窗口 - 我怀疑您最终会得到5个视图:< / p>

SubViewOne - a UserControl with the first view parts
SubViewTwo - a UserControl with the second view parts
JustViewOne - a Window containing just SubViewOne
JustViewTwo - a Window containing just SubViewTwo
BothViews - a Window containing both SubViewOne and SubViewTwo

我不认为有一种方法可以解决这样一个事实:你不希望在另一个中有一个Window ,以及顶级窗口必须是......好吧,Window

希望这会有所帮助,如果你想了解我正在做的小项目的更多细节,请告诉我 - 它远非生产质量,特别是在DI方面,但它可能足以帮助你开始。

答案 1 :(得分:2)

我想我以前做过类似于你所问的事情。我一直在玩TabControl之一,打算为我喜欢的游戏托管几种不同的工具。

主要工具是类似于通常的文件浏览器类型程序的项目浏览器,类似于Jon上面描述的内容。我将解释一些可能感兴趣/相关的部分(我已经删除了一些略显模糊的命名)。

ExplorerView标签与Jon描述的标签基本相同(希望这是一个好兆头 - 意味着我不会疯狂= D)

<UserControl x:Class="ItemsBrowser.Views.ItemsTabView"
<!-- namespaces -->
>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <ContentControl x:Name="ItemsExplorer" Grid.Column="0" Grid.Row="0" />
        <GridSplitter HorizontalAlignment="Right" VerticalAlignment="Stretch" 
                      ResizeBehavior="PreviousAndNext" Width="4" Grid.Column="1" Background="#FFAAAAAA" />
        <ContentControl x:Name="PanelView" Grid.Column="2" Grid.Row="0" />
    </Grid>   
</UserControl>

关联的ViewModel包含另外两个ViewModels,用于撰写主浏览器视图:

public class ItemsTabViewModel : Conductor<IScreen>.Collection.AllActive 
{
    public ItemsViewModel ItemsExplorer { get; set; }
    public ExplorerPanelViewModel PanelView { get; set; }

    // Ctor etc.
}

ItemsExplorer托管TreeView样式控件,允许用户从游戏中探索各种类别的Item。这在应用程序的多个位置使用,并由几个不同的控件组成。

ExplorerPanelView是右侧的面板,根据用户正在查看的项目类型,更改为显示ViewModels个数。用户还可以选择在Views中显示的ViewModel上切换一些不同的ExplorerPanelView

ExplorerPanelView看起来像:

<UserControl x:Class="MIS_PTBrochure.Views.ExplorerPanelView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:cal="http://www.caliburnproject.org">
    <Grid>
        <ContentControl cal:View.Model="{Binding Path=ActiveItem}" 
                        cal:View.Context="{Binding Path=ActiveItem.State}"
                        Content="Select a folder."/>
    </Grid>
</UserControl>

背后是ExplorerPanelViewModel

public class ExplorerPanelViewModel : Conductor<IScreen>.Collection.OneActive,
    IHandle<ItemSelectedEvent> // More events.
{
    public ItemViewModel ItemInfo { get; set; }
    public CategoryFolderViewModel CategoryFolderInfo { get; set; }

    public ExplorerPanelViewModel()
    {
        // My helper to access the `Caliburn.Micro` EventAggregator.
        EventAggregatorFactory.EventAggregator.Subscribe(this);

        // Other code
    }

    public void Handle(ItemSelectedEvent message)
    {
        // Other code to check active status
            ItemInfo = message.selected;
            ActivateItem(ItemInfo);
    }

    protected override void OnDeactivate(bool close)
    {
        Debug.WriteLine("Deactivated " + this.ToString() + close.ToString());
        if (close) { EventAggregatorFactory.EventAggregator.Unsubscribe(this); }
        base.OnDeactivate(close);
    }

    // Other code
}

我试图删除很多不相关的代码。基本上我再次托管多个ViewModels作为属性(虽然你可以拥有一个集合),并在我的ViewModel引发approriate事件时激活相关的ItemsExplorerViewModel。我正在使用Caliburn.Micro EventAggregator来处理多个ViewModels之间的通信。

理论上你可以免除属性,只需激活事件中引用的ViewModels

关于cal:View.Contextcal:View.Model - 我正在使用这些用户来切换可用的不同可用View状态(该面板中显示的每个ViewModel都是从所有ViewModel属性的基础State类。

在某些地方,我使用相同的ViewsViewModels弹出不同的窗口。为此,我使用Caliburn.Micro WindowManager。在官方文档中没有太多关于它的信息(你最好搜索谷歌和CM的讨论),它就像在锡上说的一样。

如果查看Caliburn.Micro.IWindowManager界面,您会看到一些方便的方法,您可以从WindowManager实例调用。

public interface IWindowManager
{
    bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
    void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
    void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
}

因此,为了弹出一个新的Window并选择ViewModel,我在这些方面做了一些事情:

// Some basic Window settings.
dynamic settings = new ExpandoObject();
    settings.Title = "Test Window";
    settings.WindowStartupLocation = WindowStartupLocation.Manual;
    settings.SizeToContent = SizeToContent.Manual;
    settings.Width = 450;
    settings.Height = 300;

    var TestViewModel new TestViewModel();
    WindowManagerFactory.WindowManager.ShowWindow(this.classSearch, null, settings);

Caliburn.Micro应再次将Views解析为正确的ViewModels

希望某处有一些有用的东西。我通过一些设计迭代得出了这个解决方案,所以这可能不是解决其中一些问题的最佳方法。如果有人有任何建设性的批评,请告诉我= D