每次我在选项卡之间切换时,WPF TabControl MVVM ViewModel 都会被实例化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18674275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF TabControl MVVM ViewModel get instantiated everytime i toggle betwwen tabs
提问by IdanB
I wrote some WPF application with MVVM pattern that holds a TabControl bound to collection of "TabViewModelItem".
我用 MVVM 模式编写了一些 WPF 应用程序,其中包含一个绑定到“TabViewModelItem”集合的 TabControl。
The main window XAML:
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>
and the ViewModel that hold collection of tab is code:
保存选项卡集合的 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)
}
}
So basically it's tab control that is bound to a collection of "TabViews". and based of the object data type it's showing View1 or View2. Note: View1 & View 2 are UserControls, each bound to it's own ViewModel. This concept works fine.
所以基本上它是绑定到“TabViews”集合的选项卡控件。并根据对象数据类型显示 View1 或 View2。注意:View1 和 View 2 是 UserControls,每个都绑定到它自己的 ViewModel。这个概念很好用。
Now where is the problem you my ask ? My problem is: EVERY time I click on another tab & then return to same tab, I get that specifc tab ViewModel constructor called again, where as I would expect the ViewModel object would remain.
现在你问我的问题在哪里?我的问题是:每次我点击另一个选项卡然后返回同一个选项卡时,我都会再次调用该特定选项卡的 ViewModel 构造函数,正如我所期望的那样,ViewModel 对象将保留。
This is problem, because it cause me to lose any modification made on that page, when I toggle between tabs. and since the ctor is called everytime, over & over, I can't even use the VIewModel to store this information.
这是问题,因为当我在选项卡之间切换时,它会导致我丢失在该页面上所做的任何修改。由于 ctor 每次都被调用,一遍又一遍,我什至不能使用 VIewModel 来存储这些信息。
My questions are: 1) Is there any way I can prevent the TabControl to dispose ViewModel objects when tab is inactive ? Meaning to pre-create all ViewModel's object & not dispose them when hidden ? 2) What "workarounds" using this concept exist, that allow me to store "visual tree" of the given tab, so if i navigate away from it & then re-open it, it will store all information on it (such as selected check boxes, written text, etc.)
我的问题是:1) 有什么办法可以防止 TabControl 在选项卡处于非活动状态时处理 ViewModel 对象?意思是预先创建所有 ViewModel 的对象,而不是在隐藏时处理它们?2)存在哪些使用此概念的“解决方法”,允许我存储给定选项卡的“可视化树”,因此如果我离开它然后重新打开它,它将存储其上的所有信息(例如选择复选框、书面文本等)
Would appreciate any help on matter.
将不胜感激任何帮助。
regards, Idan
问候,伊丹
采纳答案by IdanB
Solution to problem is extending TabControl and replacing default behaviour so it will not unload old tabs. The final solution (with include's both control & control template) is @ Stop TabControl from recreating its children
问题的解决方案是扩展 TabControl 并替换默认行为,因此它不会卸载旧选项卡。最终的解决方案(包含控制和控制模板)是 @StopTabControl 重新创建其子项
Thanks for Shoe for pointing me in right direction that lead to final solution :)
感谢 Shoe 为我指明了导致最终解决方案的正确方向:)
回答by Rahul W
I have the similar problem and i came up with this solution
我有类似的问题,我想出了这个解决方案
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);
}
}
I have created an event handler in my ViewModel and my View subscribes to that. My ViewModel objects get added to a collection. Now, whenever a ViewModel is added, my MainViewModel will call this even handler.
我在我的 ViewModel 中创建了一个事件处理程序,我的 View 订阅了它。我的 ViewModel 对象被添加到一个集合中。现在,每当添加 ViewModel 时,我的 MainViewModel 都会调用这个偶数处理程序。
Here, we need to find the DataTemplate that would have been defined for the DataType of the ViewModel that we are adding. Once we get hold of that, we can create a tab item and then load the content from the datatemplate.
在这里,我们需要找到为我们添加的 ViewModel 的 DataType 定义的 DataTemplate。一旦我们掌握了它,我们就可以创建一个选项卡项,然后从数据模板加载内容。
Since i am using DevExpress TabControl, i have created DXTabItem. A TabItem should also work the same.
由于我使用的是 DevExpress TabControl,因此我创建了 DXTabItem。TabItem 也应该以相同的方式工作。

