为什么Treeview是否在所有选项卡中扩展了MVVM模型继承?

时间:2015-10-14 12:22:54

标签: c# wpf mvvm treeview

我不明白为什么当我在WPF中切换选项卡的树视图的扩展时,它会影响所有选项卡的树视图的扩展。我希望每个选项卡的树视图彼此独立。这是一个非常简单的MVVM设置,只有几个类。

enter image description here

以下是项目中的文件

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:data="clr-namespace:WpfApplication1"
        xmlns:view="clr-namespace:WpfApplication1.View"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="350" Width="250">

    <Window.DataContext>
        <data:ViewModel/>
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--<Button Content="Add" Command="{Binding AddCommand}" Grid.Row="0"></Button>-->
        <TabControl x:Name="tabControl1" ItemsSource="{Binding TabItems}" Grid.Row="1" Background="LightBlue">
            <TabControl.ItemTemplate>
                <DataTemplate>
                        <TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
                </DataTemplate>
            </TabControl.ItemTemplate>

            <TabControl.ContentTemplate>
                <DataTemplate>
                    <view:TabItemView />
                </DataTemplate>
            </TabControl.ContentTemplate>


        </TabControl>

    </Grid>
</Window>

TabItemView.xaml

<UserControl x:Class="WpfApplication1.View.TabItemView"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" Text="{Binding Content}" />
        <TreeView Grid.Row="1" Background="Transparent">
            <TreeViewItem Header="Favorites">
                <TreeViewItem Header="USA"></TreeViewItem>
                <TreeViewItem Header="Canada"></TreeViewItem>
                <TreeViewItem Header="Mexico"></TreeViewItem>
            </TreeViewItem>
        </TreeView>
    </Grid>
</UserControl>

ViewModel.cs

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication1
{
    public class ViewModel
    {
        private ObservableCollection<TabItem> tabItems;

        public ObservableCollection<TabItem> TabItems
        {
            get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
        }

        public ViewModel()
        {
            TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 1"), Content = DateTime.Now.ToString("F") });
            TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 2"), Content = DateTime.Now.ToString("F") });
            TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 3"), Content = DateTime.Now.ToString("F") });
        }
    }

    public class TabItem
    {
        public string Header { get; set; }
        public string Content { get; set; }
    }
}

1 个答案:

答案 0 :(得分:0)

TabControl为每个项目重复使用相同的内容元素。它所做的就是更改绑定数据上下文(即视图模型),更新模板化元素中绑定到视图模型的元素。因此,从一个选项卡切换到下一个选项卡时,视图的其他状态保持不变。

每次更改标签时都可以强制创建新的内容元素;一种方法是将内容元素的模板声明为资源,将x:Shared="False"添加到资源声明中,然后将该资源用作Setter中应用的Style的值定位TabItem类型。

需要通过setter将模板应用到每个TabItem - 并且TabItem这里我指的是WPF TabItem,而不是您的视图模型类,我的名字是如果我是你,我会改变,以避免混淆。如果您只是直接设置x:Shared一次,则使用TabControl.ContentTemplate无效。

例如:

  <TabControl.Resources>
    <DataTemplate x:Key="tabItemTemplate" x:Shared="False">
      <l:TabItemView />
    </DataTemplate>
    <s:Style TargetType="TabItem">
      <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=tabItemTemplate}"/>
    </s:Style>
  </TabControl.Resources>

但是,这会产生相反的效果:当您从Tab切换到Tab时,不是保持每个项目的状态,而是完全重置 视图状态,因为每个都会创建一个全新的内容元素你换的时候。

如果这对你来说是可以接受的,那么这样可以正常工作。但是,如果您要查找每个选项卡以保留用户上次查看该选项卡时所处的配置,则必须自己保留该状态。例如,您可以向视图模型添加bool属性,以记住每个项目的当前设置,并将其绑定到顶级IsExpanded的{​​{1}}属性:

查看型号:

TreeViewItem

查看:

public class TabItem
{
    public string Header { get; set; }
    public string Content { get; set; }
    public bool IsExpanded { get; set; }
}

请注意,要使其正常工作,您需要将绑定<UserControl x:Class="TestSO33125188TreeViewTemplate.TabItemView" 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:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="{Binding Content}" /> <TreeView Grid.Row="1" Background="Transparent"> <TreeViewItem Header="Favorites" IsExpanded="{Binding IsExpanded, Mode=TwoWay}"> <TreeViewItem Header="USA"></TreeViewItem> <TreeViewItem Header="Canada"></TreeViewItem> <TreeViewItem Header="Mexico"></TreeViewItem> </TreeViewItem> </TreeView> </Grid> </UserControl> 属性显式设置为Mode,默认为TwoWay,否则不会复制当前{{1}返回视图模型。

另请注意,对于您的特定示例,您可以使用简单的容器视图模型,而无需实现OneWay或类似的机制。每当更改当前选项卡时,WPF都会强制将更新的属性值复制回视图。但就个人而言,我会继续添加TreeViewItem.IsExpanded实施;在WPF不会自动检测到你已经更新了属性值的不同场景中发现自己重复使用该技术会很容易,导致一个令人沮丧的错误需要一段时间来追踪和修复。