【问题标题】:WPF TabControl MVVM ViewModel get instantiated every time i toggle between tabs [duplicate]每次我在选项卡之间切换时都会实例化 WPF TabControl MVVM ViewModel [重复]
【发布时间】:2013-09-07 14:22:13
【问题描述】:

我使用 MVVM 模式编写了一些 WPF 应用程序,其中包含绑定到“TabViewModelItem”集合的 TabControl。

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>

而持有tab集合的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 和 View 2 是 UserControls,每个都绑定到自己的 ViewModel。 这个概念很好用。

现在你问我的问题在哪里? 我的问题是: 每次我单击另一个选项卡然后返回到同一个选项卡时,我都会再次调用该特定选项卡 ViewModel 构造函数,正如我所期望的那样,ViewModel 对象将保留。

这是个问题,因为当我在选项卡之间切换时,它会导致我丢失在该页面上所做的任何修改。而且由于 ctor 每次都被一遍又一遍地调用,我什至不能使用 VIewModel 来存储这些信息。

我的问题是: 1)有什么方法可以阻止 TabControl 在选项卡处于非活动状态时处理 ViewModel 对象?意味着预先创建所有 ViewModel 的对象而不是在隐藏时处理它们? 2)存在哪些使用此概念的“解决方法”,允许我存储给定选项卡的“视觉树”,因此如果我离开它然后重新打开它,它将存储所有信息(例如选择复选框、书面文本等)

如果有任何帮助,我们将不胜感激。

问候, 伊丹

【问题讨论】:

  • 我看过那篇文章,但恐怕我看不到“WpfApplicationFramework”中的“writer”示例如何解决这个问题,因为它对所有文档使用单一视图,其中- 因为我为每个选项卡使用单独的视图(每个都绑定到它自己的视图模型)。
  • 那么这个问题可能更合适。 stackoverflow.com/questions/8808076/…
  • 谢谢 :) 听起来方向正确,我会检查一下。会随着我的进展发布回复。
  • templateSelector 解决方案不起作用(仍然在每个选项卡之间切换视图模型)。我现在要试试 TabControlEx 类解决方案,会更新它
  • TabControlEx 工作,但您提供的链接没有解决它,因为它只是“部分”解决方案。它缺少可以正常工作的控件样式模板,这是我通过 google @stackoverflow.com/questions/9794151/… 发现的。再次感谢:)

标签: wpf mvvm viewmodel tabcontrol


【解决方案1】:

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

感谢 Shoe 为我指明了最终解决方案的正确方向 :)

【讨论】:

    【解决方案2】:

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

        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。一旦我们掌握了这一点,我们就可以创建一个选项卡项,然后从数据模板中加载内容。

    由于我使用的是 DevExpress TabControl,因此我创建了 DXTabItem。 TabItem 也应该同样工作。

    【讨论】:

      猜你喜欢
      • 2017-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多