- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我有一个绑定(bind)到 TabControl
的 View 模型的 IList
。此 IList
在 TabControl
的生命周期内不会更改。
<TabControl ItemsSource="{Binding Tabs}" SelectedIndex="0" >
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Content" Value="{Binding}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
每个 View 模型都有一个 DataTemplate
,它在 ResourceDictionary
中指定。
<DataTemplate TargetType={x:Type vm:MyViewModel}>
<v:MyView/>
</DataTemplate>
DataTemplate 中指定的每个 View 都是资源密集型的,足以创建我宁愿每个 View 只创建一次,但是当我切换选项卡时,相关 View 的构造函数被调用。据我所知,这是 TabControl
的预期行为,但我不清楚调用构造函数的机制是什么。
我看过a similar question which uses UserControl
s但是那里提供的解决方案将要求我绑定(bind)到不受欢迎的观点。
最佳答案
默认情况下,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 blog post还有这个answer on StackOverflow .
HTH,
关于c# - 停止 TabControl 重新创建其子项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9794151/
这个问题在这里已经有了答案: How does Scala's apply() method magic work? (3 个回答) 9年前关闭。 假设我在 scala 中有一个 MyList 类,其
这个问题在这里已经有了答案: What is a non-capturing group in regular expressions? (18 个回答) Reference - What does
这个问题是针对嵌入式系统的! 我有以下选项来初始化一个对象: Object* o = new Object(arg); 这会将对象放入堆中并返回指向它的指针。我不喜欢在嵌入式软件中使用动态分配。 Ob
我自己搜索过,没能成功的正则表达式。 我有一个 html 文件,其中包含 [] 之间的变量我想把每一个字都写进去。 [client_name][client_company] [cl
我是 Python 新手。我不明白为什么这段代码不起作用: reOptions = re.search( "[\s+@twitter\s+(?P\w+):(?P.*?)\s+]", d
在过去 7 个月左右的时间里,我几乎一直在使用 .NET C# 进行编程。在那之前,我的大部分编程都是用 C++(从学校里学的)。在工作中,我可能需要在接下来的几个月里做一大堆 C 语言。我对 C 的
我是 RE 的新手,我正在尝试获取歌词并分离出歌词标题、和声和主唱: 下面是一些歌词的例子: [Intro] D.A. got that dope! [Chorus: Travis Scott] Ic
这可能是不可能的,但我想检查是否可以用一种简单的方式表达这样的事情: // obviously doesn't work class Foo : IFoo where T: Bar {
我们的应用程序中有“user”和“study”实体,存储在它们各自的表中。一项研究代表一种研究和已收集的数据。它们是多对多的关系,所以我们需要一个链接表:studies_users。 我们为用户分配角
将测试条件添加到 Visual Studio 2010 数据库单元测试(对于 SQL Server 2008)时,这些条件称为例如rowCountCondition1、rowCountConditio
在模拟器上,我可以从设置中卸载 SD 卡。 然后我可以将它安装到我的操作系统上,然后正常卸载它。 我一直无法弄清楚如何在模拟器上重新安装它(无需重新启动)。 提示: adb 命令 remount 是无
假设在一个分支上执行了一系列提交,但该分支尚未与主干重新同步。是否可以从提交中生成全局补丁?是否可以从一系列提交中生成“分组”补丁?如果是,如何? 最佳答案 svn diff -rXXX:YYY UR
在某些情况下,我想在我的应用程序中锁定调整大小功能,为此我尝试对属性进行数据绑定(bind),并且不允许在某些情况下更改它,但没有成功。 有没有办法这样做? 这是我不成功的尝试: XAML: Vie
当我的计算机连接多个显示器时,我可以检测它们,并根据从获取的值设置位置来向它们绘制图形 get(0, 'MonitorPositions') 但是,当我在 MATLAB 运行时断开监视器时,此属性不会
我们有一个grails应用程序,该应用程序在grails数据库中存储了各种域对象。该应用程序连接到第二个数据库,运行一些原始sql,并在表中显示结果。它基本上是一个报告服务器。 我们通过在DataSo
无法比较来自不同容器的迭代器(参见这里的示例: https://stackoverflow.com/a/4664519/225186 )(或者从技术上讲,它不需要有意义。) 这就提出了另一个问题,来自
我有以下情况: 家长 Activity : ParentActivityClass { private Intent intent; @Override public void onCreate(Bu
我经常将元素与附加功能 Hook ,例如: $('.myfav').autocomplete(); $('.myfav').datepicker(); $('.myfav').click(somefu
因此,我将 tooltipster.js 库用于工具提示,并尝试更改工具提示在不同屏幕尺寸上的默认距离。 所以这是默认的 init 的样子: $(inputTooltipTrigger).tool
我在 ARM7 嵌入式环境中工作。我使用的编译器不支持完整的 C++ 功能。它不支持的一项功能是动态类型转换。 有没有办法实现dynamic_cast<>() ? 我使用 Google 寻找代码,但到
我是一名优秀的程序员,十分优秀!