有没有简单的方法来做到这一点。
问题是你有一个WPF模板,这意思是一样的,不管你把什么数据背后的。因此,创建了该模板的一个副本,并且随时WPF在您的UI树中遇到ListViewModel
时,它将使用该模板进行绘制。未绑定到DataContext的控件的属性将保持其在更改DataSources之间的状态。
你可以使用x:Shared="False"
(例如here),但是这将创建模板随时随地的新副本WPF要求它,当你切换标签页,其中包括。
当[x:Shared
时]设置为false,修改的Windows Presentation Foundation(WPF)资源检索行为,使得对于资源请求将创建一个新的实例为每个请求,而不是共享同一个实例的所有请求。
你真正需要的是为TabControl.Items
每个生成您控制每个项目的新副本,但是当你使用ItemsSource
属性(这是由设计)不会发生。
一个可能的替代方法可能是创建绑定到您的项目集合的自定义DependencyProperty,并为集合中的每个项目生成TabItem
和UserControl
对象。此自定义DP还需要处理收集更改事件以确保TabItem与您的收藏保持同步。
这里有一个我一直在玩弄。它适用于简单的情况,例如绑定到ObservableCollection,以及添加/删除项目。从XAML像这样使用
public class TabControlHelpers
{
// Custom DependencyProperty for a CachedItemsSource
public static readonly DependencyProperty CachedItemsSourceProperty =
DependencyProperty.RegisterAttached("CachedItemsSource", typeof(IList), typeof(TabControlHelpers), new PropertyMetadata(null, CachedItemsSource_Changed));
// Get
public static IList GetCachedItemsSource(DependencyObject obj)
{
if (obj == null)
return null;
return obj.GetValue(CachedItemsSourceProperty) as IList;
}
// Set
public static void SetCachedItemsSource(DependencyObject obj, IEnumerable value)
{
if (obj != null)
obj.SetValue(CachedItemsSourceProperty, value);
}
// Change Event
public static void CachedItemsSource_Changed(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (!(obj is TabControl))
return;
var changeAction = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
var tabControl = obj as TabControl;
if (tabControl != null)
UpdateTabItems(tabControl);
});
// if the bound property is an ObservableCollection, attach change events
INotifyCollectionChanged newValue = e.NewValue as INotifyCollectionChanged;
INotifyCollectionChanged oldValue = e.OldValue as INotifyCollectionChanged;
if (oldValue != null)
newValue.CollectionChanged -= changeAction;
if (newValue != null)
newValue.CollectionChanged += changeAction;
UpdateTabItems(obj as TabControl);
}
static void UpdateTabItems(TabControl tc)
{
if (tc == null)
return;
IList itemsSource = GetCachedItemsSource(tc);
if (itemsSource == null || itemsSource.Count == null)
{
if (tc.Items.Count > 0)
tc.Items.Clear();
return;
}
// loop through items source and make sure datacontext is correct for each one
for(int i = 0; i < itemsSource.Count; i++)
{
if (tc.Items.Count <= i)
{
TabItem t = new TabItem();
t.DataContext = itemsSource[i];
t.Content = new UserControl1(); // Should be Dynamic...
tc.Items.Add(t);
continue;
}
TabItem current = tc.Items[i] as TabItem;
if (current == null)
continue;
if (current.DataContext == itemsSource[i])
continue;
current.DataContext = itemsSource[i];
}
// loop backwards and cleanup extra tabs
for (int i = tc.Items.Count; i > itemsSource.Count; i--)
{
tc.Items.RemoveAt(i - 1);
}
}
}
它:
<TabControl local:TabControlHelpers.CachedItemsSource="{Binding Values}">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding SomeString}" />
</Style>
</TabControl.Resources>
</TabControl>
有几件事情需要注意:
TabItem.Header
没有设置,所以你必须设置为它绑定在TabControl.Resources
- 的DependencyProperty目前执行的硬编码用户控件新的创造。可能需要做一些其他的方式,比如试图用一个模板属性或者一个不同的DP告诉它要创建的用户控件
- 可能会需要更多的测试...不知道是否有任何问题,由于改变处理器的内存泄漏等
@ MM8我认为这是一个不正确的重复链接。这个问题是关于显示每个标签的项目,而不是创建一个单一的项目,只是调换的DataContext他们身后的默认创建一个单独的ContentTemplate。我认为可能有一个模板属性来执行此操作,但我不确定。就我个人而言,我会小心这样的设计......即使它不可见,您也会创建/存储控件的多个副本。如果大小很重要,你可以在你的DataContext上为它创建一个属性并绑定它,这样它随着每个制表符的变化而变化。 – Rachel
我想的特性是'X:共享= “假”'(例如[这里](http://stackoverflow.com/a/3488396/302677))。但这并不是很理想,因为每次选择该选项卡时都会创建一个UserControl的新副本,因此不管大小更改如何都不会保留。如果您使用模板构建TabControl项目,我建议仅存储/绑定您关心的所有属性,以便在用户切换选项卡时使用与其使用的模板相同的模板,但DataContext不同,因此所有绑定都会更新 – Rachel
到一个自定义的TabControl DependencyProperty中,为每个项目构建'.TabItems',而不是使用'ItemsSource'? – Rachel