【问题标题】:Unable to add two UserControls in the same view无法在同一视图中添加两个用户控件
【发布时间】:2012-11-17 09:37:44
【问题描述】:

我有一个列表视图,其中有两个子视图。一个是显示视图,另一个是编辑视图。这是我定义列表(父)视图的方式。请注意,我希望两个子用户控件在父控件中占据不同的空间。

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
    ....

    <ContentControl x:Name="GroupDetail" Grid.Row="2" />
    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0"/>
</UserControl>

然后在我的视图模型中,我根据用户响应以下列方式激活这些项目

**查看模型**

[Export(typeof(RelayListViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class RelayListViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<Group> {
    ....
    public void Edit() { //Requested Edit
        RelayEditViewModel viewModel = TryAndLocateViewModel(SelectedRelay.Group.Rack.Id, SelectedRelay.Group.Id);
        ActivateItem(viewModel);
    }
    ....

    public void ViewGroupDetail(Relay relay) { //Requested View
        GroupDetailViewModel viewModel = container.GetExportedValue<GroupDetailViewModel>();
        ActivateItem(viewModel);
    }

上面的代码可以工作,但细节视图加载在选项卡空间(用于编辑视图的空间)中。实际上,ActivateItem(viewModel) 确实选择了正确类型的视图来显示,但它被加载到了显示视图的错误位置,即显示视图加载到了屏幕上编辑视图的空间中。当然,我错过了一些明显的东西。

综上所述,我们如何让定义在 Parent UserControl 中的两个 UserControl 在自己的空间中激活?

编辑 - 1:

这里有两个屏幕截图,分别显示了我需要在哪里加载 EditDetail 视图。

正如您在第二个屏幕截图中看到的那样,详细信息视图被加载到详细信息区域以及编辑区域(选项卡)中。我不希望详细视图仅出现在详细信息区域中。编辑区仅用于编辑视图。

这是我用来生成屏幕截图的代码。

包含两个视图的主视图

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
    <Grid>
         ....
                     <ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" 
                                        cal:View.Context="GroupDetail" cal:View.Model="{Binding ActiveItem}"/>
                    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0"
                                cal:View.Context="RelayEdit" cal:View.Model="{Binding ActiveItem}"/>
     </Grid>
</UserControl>

编辑 2: 我想我已经很接近让它工作了。根据您的建议,我修改了 Main(Parent) 容器,如下所示。

<UserControl x:Class="RelayAnalysis_UI.Views.Relays.RelayListView"
            <ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" />
                <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource TabControlStyle}" Margin="5,0,0,0" />

编辑屏幕和详细信息屏幕现在出现在适当的位置。但是,没有调用 Detail ViewModels OnActivate,所以我得到一个没有填充变量的空白 Detail View。详细信息视图字段的所有加载都在 OnActivate() 覆盖上完成。这是我的 GroupDetailViewModel 的定义方式

[Export(typeof(GroupDetailViewModel))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class GroupDetailViewModel : Screen {
    ...
    protected override void OnActivate() {
        base.OnActivate();
    ..
    } 

当然,我缺少一些属性。还是我必须在 GroupDetailViewModel 上调用一些方法来手动加载详细信息?

【问题讨论】:

    标签: wpf caliburn.micro wpf-4.0 caliburn


    【解决方案1】:

    删除了原来的答案,因为它很长而且没有多大帮助

    编辑:

    好吧,所以忽略上述 - 看起来您正试图在两个不同的视图模型上加载两个不同的视图,据我所知,这不是 Context 的设计目的。 Context 属性在同一视图模型上加载两个不同的视图,例如在您的 XAML 中:

    <ContentControl x:Name="GroupDetail" HorizontalContentAlignment="Left" 
        cal:View.Context="GroupDetail" 
        cal:View.Model="{Binding ActiveItem}"/>
    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource  TabControlStyle}" Margin="5,0,0,0" 
        cal:View.Context="RelayEdit" 
        cal:View.Model="{Binding ActiveItem}"/>
    

    给定一个通过ActivateItem() 激活的名为RelayEditViewModel 的VM,CM 将尝试加载以下视图:

    RelayEdit.GroupDetail 用于内容控件

    RelayEdit.RelayEdit 用于选项卡控件

    见:

    http://caliburnmicro.codeplex.com/wikipage?title=View%2fViewModel%20Naming%20Conventions&referringTitle=Documentation

    ...

    如果您尝试加载另一个 ViewModel,则将适用相同的约定来查找视图

    GroupDetailViewModel 结果

    GroupDetail.GroupDetail 用于内容控件

    GroupDetail.RelayEdit 用于选项卡控件

    听起来这不是您想要的(而且我不确定为什么要加载任何内容 - 您的视图在哪个命名空间中?您是否自定义了视图定位器?)

    我仍在努力了解您需要的生命周期支持,但听起来您希望编辑视图由生命周期管理(因为您希望加载/保存/保护类型支持)但详细视图是是只读的,不关心它是否在没有被看守的情况下关闭

    在这种情况下,您可能希望向 ViewModel 添加一个属性,该属性将包含对详细视图模型的引用但不要激活它...只需设置属性而不调用 ActivateItem(vm)

    示例:

    [Export(typeof(RelayListViewModel))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class RelayListViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<Group> {
    ....
    // Backing field + prop to hold the details view - the content control will bind to this
    private IScreen _details;
    public IViewAware Details { get { } set { } } // Implement standard NotifyOfPropertyChange here for this property
    
    public void Edit() { //Requested Edit
        RelayEditViewModel viewModel = TryAndLocateViewModel(SelectedRelay.Group.Rack.Id, SelectedRelay.Group.Id);
        ActivateItem(viewModel);
    }
    ....
    
    public void ViewGroupDetail(Relay relay) { //Requested View
        GroupDetailViewModel viewModel = container.GetExportedValue<GroupDetailViewModel>();
        // Instead of activating, just assign the VM to the property and make sure Details calls NotifyOfPropertyChange to let CM know to start the binding logic
        Details = viewModel;
    }
    

    然后在你的 XAML 中

    <!-- Just bind the details view to the Details property -->
    <ContentControl x:Name="Details" HorizontalContentAlignment="Left" /> 
    <!-- Leave this as-is, as it's working ok -->
    <TabControl x:Name="Items" Grid.Column="0" Style="{StaticResource  TabControlStyle}" Margin="5,0,0,0" /> 
    

    (我假设您使用的是上面的TabControls 默认约定,但如有必要,请进行调整)

    只要您相应地设置了Context 属性,您就可以将同一虚拟机用于详细信息和编辑视图。

    如果有帮助请告诉我

    编辑:

    只是为了回答关于MVVM和耦合等的问题......

    您所做的只是从几个更简单的视图模型(因此从几个更简单的视图中组合一个更复杂的视图)组成一个更复杂的视图模型。只要您对详细信息 VM 的引用不是具体类型,VM 之间的耦合就会非常松散。您可以将实现该接口的任何视图模型类型分配到主 VM 上的 Detail 属性中,CM 将尝试为其定位视图并构建接口。这很好(如果需要,您可以使用 IoC 获取详细信息窗口的类型)

    如果您的详细信息视图需要生命周期,您应该从 Screen 继承,但我怀疑您的详细信息视图需要激活(因为它只是一个详细信息视图并且仅准备就绪)所以只需实现 IViewAware 并从 PropertyChangedBase 继承会做。然而,编辑视图需要有生命周期,因此应该继承自 Screen.

    Conductor 已经包含一个ActiveItem 属性,并为通过ActivateItem() 激活的子项提供生命周期管理,您所要做的就是为您的导体创建一个额外的“螺栓固定”属性,该属性引用额外的虚拟机(即你需要ActiveItemDetails

    【讨论】:

    • 我已编辑问题以包含屏幕截图。我需要有两个活动项目,一个用于编辑,一个用于细节,但它们应该出现在各自的区域(父视图中的两个不同区域)中,如屏幕截图所示。
    • 谢谢您 - 现在我可以看到它更有意义了,您正在使用 Context 属性来选择要加载的视图
    • 是的,我实际上是根据我在网上发现的内容修改了我的代码。但是我仍然没有得到 DetailView 应该在 Detail Area 中加载和 Edit View(s) 应该在 EditArea 中加载(作为选项卡)的所需行为。详细视图仍加载在编辑区域中,当我尝试加载编辑屏幕时,它会清除详细区域中的详细视图
    • 您是否自定义了视图定位器?您是否尝试使用上下文?您正在两个 VM 上调用 ActivateItem,因此编辑屏幕或详细信息屏幕将出现在两个位置。检查我的编辑(如果您还没有) - 您不希望仅查看屏幕的生命周期,因此您希望使用 VM 上的属性而不是使用 ActivateItem 来绑定它(即不要通过 ActivateItem 激活详细信息 VM , 如果它实现了 IActivatable,只需调用 Activate)
    • 如果我们不使用ActivateItem,我们如何确保在Detail ViewModel上调用OnActivate()。请参阅我的编辑 2. IActivatable 是 Caliburn.Micro 的一部分。我没有在我的代码中看到任何用于解析引用的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多