阻止TabControl重新创建其子级
我有一个IList
绑定到的viewmodel TabControl
。这IList
不会在的使用期限内发生变化TabControl
。
<TabControl ItemsSource="{Binding Tabs}" SelectedIndex="0" >
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Content" Value="{Binding}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
每个视图模型都有一个DataTemplate
在中指定的ResourceDictionary
。
<DataTemplate TargetType={x:Type vm:MyViewModel}>
<v:MyView/>
</DataTemplate>
DataTemplate中指定的每个视图都占用大量资源,以至于我宁愿只创建一次每个视图,但是当我切换选项卡时,将调用相关视图的构造函数。根据我的阅读,这是的预期行为TabControl
,但是对我来说,尚不清楚调用构造函数的机制是什么。
我看过一个类似的问题,它使用了UserControl
s,但是那里提供的解决方案将需要我绑定到视图,这是不希望的。
-
默认情况下,
TabControl
共享面板以呈现其内容。若要做您想做的事情(以及许多其他WPF开发人员),您需要TabControl
像这样进行扩展:TabControlEx.cs
[TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))] public class TabControlEx : TabControl { private Panel ItemsHolderPanel = null; public TabControlEx() : base() { // This is necessary so that we get the initial databound selected item ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; } /// <summary> /// If containers are done, generate the selected item /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; UpdateSelectedItem(); } } /// <summary> /// Get the ItemsHolder and generate any children /// </summary> public override void OnApplyTemplate() { base.OnApplyTemplate(); ItemsHolderPanel = GetTemplateChild("PART_ItemsHolder") as Panel; UpdateSelectedItem(); } /// <summary> /// When the items change we remove any generated panel children and add any new ones as necessary /// </summary> /// <param name="e"></param> protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { base.OnItemsChanged(e); if (ItemsHolderPanel == null) return; switch (e.Action) { case NotifyCollectionChangedAction.Reset: ItemsHolderPanel.Children.Clear(); break; case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Remove: if (e.OldItems != null) { foreach (var item in e.OldItems) { ContentPresenter cp = FindChildContentPresenter(item); if (cp != null) ItemsHolderPanel.Children.Remove(cp); } } // Don't do anything with new items because we don't want to // create visuals that aren't being shown UpdateSelectedItem(); break; case NotifyCollectionChangedAction.Replace: throw new NotImplementedException("Replace not implemented yet"); } } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { base.OnSelectionChanged(e); UpdateSelectedItem(); } private void UpdateSelectedItem() { if (ItemsHolderPanel == null) return; // Generate a ContentPresenter if necessary TabItem item = GetSelectedTabItem(); if (item != null) CreateChildContentPresenter(item); // show the right child foreach (ContentPresenter child in ItemsHolderPanel.Children) child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed; } private ContentPresenter CreateChildContentPresenter(object item) { if (item == null) return null; ContentPresenter cp = FindChildContentPresenter(item); if (cp != null) return cp; // the actual child to be added. cp.Tag is a reference to the TabItem cp = new ContentPresenter(); cp.Content = (item is TabItem) ? (item as TabItem).Content : item; cp.ContentTemplate = this.SelectedContentTemplate; cp.ContentTemplateSelector = this.SelectedContentTemplateSelector; cp.ContentStringFormat = this.SelectedContentStringFormat; cp.Visibility = Visibility.Collapsed; cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); ItemsHolderPanel.Children.Add(cp); return cp; } private ContentPresenter FindChildContentPresenter(object data) { if (data is TabItem) data = (data as TabItem).Content; if (data == null) return null; if (ItemsHolderPanel == null) return null; foreach (ContentPresenter cp in ItemsHolderPanel.Children) { if (cp.Content == data) return cp; } return null; } protected TabItem GetSelectedTabItem() { object selectedItem = base.SelectedItem; if (selectedItem == null) return null; TabItem item = selectedItem as TabItem; if (item == null) item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem; return item; } }
XAML
<Style TargetType="{x:Type controls:TabControlEx}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TabControl}"> <Grid Background="{TemplateBinding Background}" ClipToBounds="True" KeyboardNavigation.TabNavigation="Local" SnapsToDevicePixels="True"> <Grid.ColumnDefinitions> <ColumnDefinition x:Name="ColumnDefinition0" /> <ColumnDefinition x:Name="ColumnDefinition1" Width="0" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition x:Name="RowDefinition0" Height="Auto" /> <RowDefinition x:Name="RowDefinition1" Height="*" /> </Grid.RowDefinitions> <DockPanel Margin="2,2,0,0" LastChildFill="False"> <TabPanel x:Name="HeaderPanel" Margin="0,0,0,-1" VerticalAlignment="Bottom" Panel.ZIndex="1" DockPanel.Dock="Right" IsItemsHost="True" KeyboardNavigation.TabIndex="1" /> </DockPanel> <Border x:Name="ContentPanel" Grid.Row="1" Grid.Column="0" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local"> <Grid x:Name="PART_ItemsHolder" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
注意: 我没有提出这个解决方案。它已在编程论坛中共享了几年,并且相信它现在已成为WPF食谱书之一。我认为最古老或原始的来源是PluralSight
.NET博客文章,以及StackOverflow上的答案。HTH,
-
Sql Server 'Saving changes is not allowed' 错误——阻止保存需要重新创建表的更改
2022-03-22 关注 0 浏览42 1答案
-
如何通过转换子元素来重新创建透视图效果?
2021-02-02 关注 0 浏览131 1答案
-
从 IEnumerable 重新创建字典>
2022-07-28 关注 0 浏览17 1答案
-
创建错的表格我可以删除重新创建。
2022-05-22 关注 0 浏览14 1答案
-
Java Spring重新创建特定的Bean
2021-01-30 关注 0 浏览129 1答案
-
Kubernetes pod 在删除时重新创建
2022-07-28 关注 0 浏览8 1答案
-
直接在Python中重新创建Postgres COPY?
2021-01-29 关注 0 浏览58 1答案
-
重新创建jQuery的ajaxStart和ajaxComplete功能
2021-01-31 关注 0 浏览89 1答案
-
为什么在重新启动线程时需要重新创建实例?
2021-01-29 关注 0 浏览81 1答案
-
Java从哈希码重新创建字符串
2021-01-30 关注 0 浏览104 1答案