2

假设我有一个TabControl呈现Foo对象集合的 a (每个都通过 a FooViewModel)。在选项卡项列表的末尾,我想要一个不模拟任何内容的人造选项卡项,但它会创建一个新模型项并在单击时将其添加到集合中。

如果概念不清楚,一个真实的例子是 Internet Explorer 中的选项卡。它有n+1选项卡项:n具有页面内容,最后一个添加了新的“真实”选项卡。

在 MVVM 中对这种类型的交互进行建模的正确方法(如果有)是什么?我想到的选项是:

  • 使其成为视图模型的一部分。在控件绑定到的集合的末尾IEnumerable<FooViewModel> Foos,添加一个“新项目”哨兵并将“我是真正的 foo 还是新的 foo 哨兵”逻辑构建到视图模型中。

  • 使其完全成为视图的一部分。重新模板(和/或子类)TabControl以呈现所有真实项目,然后还有一个按钮,该按钮调用命令以创建和插入新项目。

第一个选项一开始感觉不对,就像它会将视图细节泄漏到视图模型中('add a new' 似乎很可能Foo是一个通用命令,并且某些视图可能根本不希望从选项卡列表中调用它)。但这确实有一定的意义,因为对于初始化过程,我已经需要为“半构造Foo”建模,因此“尚不存在Foo”的模型似乎并不遥远。

第二部分似乎需要做很多工作,也很容易搞砸(假设希望它看起来和感觉像其他选项卡)。

但我是 MVVM 的新手;这肯定经常出现。也许我完全错过了一些东西。传统的处理方式是什么?

4

3 回答 3

1

MVVM 模式的几个关键租户是:

  • 可测试性——通过使您的视图尽可能简单,通过将逻辑移动到您的视图模型中,您可以通过单元测试增加可以测试的逻辑量。
  • Designer-Support - 模拟视图模型可用于为您的视图提供设计时数据。

您的哪个建议最能满足上述要求?我会说选项(1)。您可以编写单元测试以确保列表中的最后一项始终是“哨兵”项。

为了“在视图模型中构建‘我是一个真正的 foo 还是一个新的 foo 哨兵’逻辑。”,您可以简单地使用类型化的 DataTemplates。

于 2012-05-21T21:18:26.663 回答
1

仅重新模板 TabControl 并在选项卡右侧添加一个人造选项卡或按钮可能会更简单。

基本示例:

<Window x:Class="ContextMenuSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <SolidColorBrush x:Key="WindowBackgroundBrush"
                         Color="#FFF" />

        <SolidColorBrush x:Key="SolidBorderBrush"
                         Color="#888" />

        <SolidColorBrush x:Key="DisabledForegroundBrush"
                         Color="#888" />

        <SolidColorBrush x:Key="DisabledBorderBrush"
                         Color="#AAA" />

        <Style  TargetType="{x:Type TabControl}">
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="SnapsToDevicePixels"
                    Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabControl}">
                        <Grid KeyboardNavigation.TabNavigation="Local">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TabPanel Name="HeaderPanel"
                                      Grid.Row="0"
                                      Panel.ZIndex="1"
                                      Margin="0,0,4,-1"
                                      IsItemsHost="True"
                                      KeyboardNavigation.TabIndex="1"
                                      Background="Transparent" />
                            <Button Grid.Row="0"
                                    Grid.Column="1"
                                    Content="Add new" />
                            <Border Name="Border"
                                    Grid.Row="1"
                                    Grid.ColumnSpan="2"
                                    Background="{StaticResource WindowBackgroundBrush}"
                                    BorderBrush="{StaticResource SolidBorderBrush}"
                                    BorderThickness="1"
                                    CornerRadius="2"
                                    KeyboardNavigation.TabNavigation="Local"
                                    KeyboardNavigation.DirectionalNavigation="Contained"
                                    KeyboardNavigation.TabIndex="2">
                                <ContentPresenter Name="PART_SelectedContentHost"
                                                  Margin="4"
                                                  ContentSource="SelectedContent" />
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled"
                                     Value="False">
                                <Setter Property="Foreground"
                                        Value="{StaticResource DisabledForegroundBrush}" />
                                <Setter TargetName="Border"
                                        Property="BorderBrush"
                                        Value="{StaticResource DisabledBorderBrush}" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>

    <Grid>
        <TabControl>
            <TabItem Header="Item 1" />
            <TabItem Header="Item 2" />
        </TabControl>
    </Grid>

</Window>

enter code here
于 2012-05-23T11:31:05.293 回答
0

我会选择第一个:

所以你有一个集合TabViews,如果它是最后一个,当它被激活时会创建一个新的并在它自己之前注入它。

样式怎么样:如果你想让它成为 UI 的一部分,你也可以在之后做。那是强项WPF

所以总的来说,解决方案变成了你所说的两种解决方案的合并。没有什么可以阻止您为TabView 集合中的最后一个制作样式模板并将其应用于它。

希望这可以帮助。

于 2012-05-21T20:57:46.023 回答