Saturday, 7 September 2013

WPF TabControl MVVM ViewModel get instantiated everytime i toggle betwwen tabs

WPF TabControl MVVM ViewModel get instantiated everytime i toggle betwwen
tabs

I wrote some WPF application with MVVM pattern that holds a TabControl
bound to collection of "TabViewModelItem".
The main window 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:
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.
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.
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.
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.)
Would appreciate any help on matter.
regards, Idan

No comments:

Post a Comment