0

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

<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:

这是两个屏幕截图,分别显示了我需要加载编辑视图和详细视图的位置。

已加载编辑屏幕

查看屏幕已加载(不应在编辑区域中加载)

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

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

包含两个视图的主视图

<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,因此我得到了一个空白的详细视图,没有填充任何变量。详细信息视图字段的所有加载都在OnActivate()覆盖上完成。这是我的 GroupDetailViewModel 的定义方式

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

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

4

1 回答 1

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}"/>

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

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相应地设置属性,您就可以将同一 VM 用于详细信息和编辑视图。

让我知道这是否有帮助

编辑:

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

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

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

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

于 2012-11-30T11:10:06.760 回答