每次我在选项卡之间切换时,WPF TabControl MVVM ViewModel都会被实例化

时间:2013-09-07 14:22:13

标签: wpf mvvm viewmodel tabcontrol

我用MVVM模式编写了一些WPF应用程序,它将TabControl绑定到“TabViewModelItem”的集合。

主窗口XAML:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ViewModel="clr-namespace:XcomSavesGameEditor.ViewModel"
        x:Class="XcomSavesGameEditor.MainWindow"
        xmlns:Views="clr-namespace:XcomSavesGameEditor.View"
        Title="X-COM Saved Game Editor" Height="650" Width="850" Background="#FF1B0000">
    <Window.DataContext>
        <ViewModel:TabsManagerViewModel/>
    </Window.DataContext>
    <Grid>

... (some not relevant code removed for clearity of question) ...

<TabControl x:Name="myTabs" Background="Black" Margin="0,25,0,0" BorderThickness="0,0,0,0" BorderBrush="Black" ItemsSource="{Binding Tabs}" >

            <TabControl.Resources>
                <DataTemplate DataType="{x:Type ViewModel:Tab0a_FileSaveData_ViewModel}">
                    <Views:Tab0a_FileSaveData_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab0b_Summary_ViewModel}">
                    <Views:Tab0b_Summary_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab1_Research_ViewModel}">
                    <Views:Tab1_Research_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab2_Engineering_ViewModel}">
                    <Views:Tab2_Engineering_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab3_Barracks_ViewModel}">
                    <Views:Tab3_Barracks_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab4_Hangar_ViewModel}">
                    <Views:Tab4_Hangar_View />
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModel:Tab5_SituationRoom_ViewModel}">
                    <Views:Tab5_SituationRoom_View />
                </DataTemplate>
            </TabControl.Resources>

            <TabControl.ItemTemplate>
                <!-- this is the header template-->
                <DataTemplate>
                    <Grid Margin="0">
                        <Border Margin="0,0,0,0" 
                                Background="Black"
                                BorderBrush="Black" 
                                BorderThickness="0,0,0,0" Padding="0,0,0,0">
                            <StackPanel   Orientation="Horizontal"
                                            Margin="0,0,0,0">
                                <Image Name ="tabImage" Source="{Binding TabImage_Disabled}" />
                            </StackPanel>
                        </Border>
                    </Grid>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent}}" Value="True">
                            <Setter TargetName="tabImage" Property="Source" Value="{Binding TabImage_Enabled}"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <!-- this is the body of the TabItem template-->
                <DataTemplate>
                    <Grid>
                        <Grid.Background>
                            <ImageBrush ImageSource="{Binding TabImage_Background}"/>
                        </Grid.Background>
                        <UniformGrid>
                            <ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="{Binding TabContents}" />
                        </UniformGrid>
                    </Grid>
                </DataTemplate>
            </TabControl.ContentTemplate>
       </TabControl>

并且包含选项卡集合的ViewModel是代码:

 public sealed class TabsManagerViewModel : ViewModelBase
 {

private ObservableCollection<TabViewModelItem> _tabs;

        public ObservableCollection<TabViewModelItem> Tabs
        {
            get { return _tabs; }
            set
            {
                _tabs = value;
                RaisePropertyChanged("Tabs");
            }
        }

        public TabsManagerViewModel()
        {
            Tabs = new ObservableCollection<TabViewModelItem>();
            Tabs.Add(new TabViewModelItem { TabName = "File_Save_Data", TabImage_Enabled = _aEnabledTabImages[(int)enum_Tabs.SaveFileData_Tab], TabImage_Disabled = _aDisabledTabImages[(int)enum_Tabs.SaveFileData_Tab], TabImage_Background = _aBackgroundTabImages[(int)enum_Tabs.SaveFileData_Tab], TabContents = new Tab0a_FileSaveData_ViewModel() });
            Tabs.Add(new TabViewModelItem { TabName = "Summary", TabImage_Enabled = _aEnabledTabImages[(int)enum_Tabs.Summary_Tab], TabImage_Disabled = _aDisabledTabImages[(int)enum_Tabs.Summary_Tab], TabImage_Background = _aBackgroundTabImages[(int)enum_Tabs.Summary_Tab], TabContents = new Tab0b_Summary_ViewModel() });

... (rest of code removed for clearity of question)

        }

}

所以基本上它的标签控件绑定到“TabViews”的集合。 并且基于对象数据类型,它显示了View1或View2。 注意:View1&amp; View 2是UserControls,每个都绑定到它自己的ViewModel。 这个概念很好。

现在我问的问题在哪里? 我的问题是: 每次我点击另一个标签&amp;然后返回到相同的选项卡,我得到再次调用的特定选项卡ViewModel构造函数,我希望ViewModel对象可以保留。

这是一个问题,因为当我在标签之间切换时,它会导致我丢失对该页面所做的任何修改。并且因为每次都会调用ctor,over&amp;结束,我甚至无法使用VIewModel来存储这些信息。

我的问题是: 1)当tab处于非活动状态时,有什么方法可以阻止TabControl配置ViewModel对象?意味着预先创建所有ViewModel的对象&amp;隐藏时不要丢弃它们? 2)使用这个概念的“变通方法”存在,允许我存储给定标签的“可视树”,所以如果我离开它并且&amp;然后重新打开它,它会存储所有信息(如选中的复选框,书面文字等)。

感谢您对此事的任何帮助。

的问候, 伊詹

2 个答案:

答案 0 :(得分:3)

问题的解决方案是扩展TabControl并替换默认行为,以便它不会卸载旧标签。 最终解决方案(包含控件和控件模板)是@ Stop TabControl from recreating its children

感谢Shoe指出我正确的方向导致最终解决方案:)

答案 1 :(得分:0)

我有类似的问题,我提出了这个解决方案

    void OnAddWorkSpace(object Sender, EventArgs e)
    {
        WorkspaceViewModel loclViewModel = (e as WorkSpaceEventArgs).ViewModel;
        DataTemplate loclTemplate = (DataTemplate)Resources[new DataTemplateKey(loclViewModel.GetType())];

        if (loclTemplate != null)
        {
            DXTabItem loclTabItem = new DXTabItem();
            loclTabItem.Content = loclTemplate.LoadContent();
            (loclTabItem.Content as DependencyObject).SetValue(DataContextProperty, loclViewModel);
            loclTabItem.HeaderTemplate = (DataTemplate)FindResource("WorkspaceItemTemplate");
            loclTabItem.Header = (loclTabItem.Content as DependencyObject).GetValue(DataContextProperty);
            MainContentTabs.Items.Add(loclTabItem);
        }
    }

我在ViewModel中创建了一个事件处理程序,我的View订阅了它。我的ViewModel对象被添加到集合中。现在,每当添加ViewModel时,我的MainViewModel都会调用这个偶数处理程序。

在这里,我们需要找到为我们正在添加的ViewModel的DataType定义的DataTemplate。一旦我们掌握了这一点,我们就可以创建一个标签项,然后从datatemplate加载内容。

由于我使用的是DevExpress TabControl,我创建了DXTabItem。 TabItem也应该一样。