WPF TabControl MVVM ViewModel создается каждый раз, когда я переключаю между вкладками
Я написал некоторое приложение WPF с шаблоном MVVM, которое удерживает 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 и View 2 - это пользовательские элементы управления, каждый из которых связан со своей собственной моделью представления. Эта концепция отлично работает.
Теперь, где проблема, которую вы спрашиваете? Моя проблема: КАЖДЫЙ раз, когда я нажимаю на другую вкладку, а затем возвращаюсь на ту же вкладку, я снова получаю эту конкретную вкладку конструктора ViewModel, где, как и следовало ожидать, объект ViewModel останется.
Это проблема, потому что это приводит к тому, что я теряю любые изменения, сделанные на этой странице, когда я переключаюсь между вкладками. и поскольку ctor вызывается каждый раз, снова и снова, я даже не могу использовать VIewModel для хранения этой информации.
Мои вопросы: 1) Можно ли как-то запретить TabControl распоряжаться объектами ViewModel, когда вкладка неактивна? Вы хотите предварительно создать все объекты ViewModel и не удалять их, когда они скрыты? 2) Какие существуют "обходные пути", использующие эту концепцию, которые позволяют мне хранить "визуальное дерево" данной вкладки, поэтому, если я отойду от нее и затем снова открою ее, она сохранит всю информацию на ней (например, выбранную). флажки, письменный текст и т. д.)
Буду признателен за любую помощь по этому вопросу.
С уважением, Идан
2 ответа
Решением проблемы является расширение TabControl и замена поведения по умолчанию, чтобы он не выгружал старые вкладки. Окончательное решение (с включенным шаблоном элемента управления и контроля) - @ Stop TabControl для воссоздания его дочерних элементов.
Спасибо за Чистку за то, что указал мне правильное направление, которое привело к окончательному решению:)
У меня похожая проблема, и я придумал это решение
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 будет вызывать этот даже обработчик.
Здесь нам нужно найти DataTemplate, который был бы определен для DataType модели ViewModel, которую мы добавляем. Как только мы это получим, мы можем создать элемент вкладки, а затем загрузить содержимое из таблицы данных.
Так как я использую DevExpress TabControl, я создал DXTabItem. TabItem также должен работать так же.