TabControl的 ItemsSource 属性绑定到ViewModel中的集合。 ContentTemplate 是 ListView - UserControl。所有选项卡只使用一个ListView控件( ListView 的构造函数只调用一次)。问题是所有选项卡都具有共同的可视状态 - 例如,如果更改一个选项卡中任何项目的大小,则此更改将在所有选项卡上进行。如何为每个标签创建单独的 ListView ,但同时使用 ItemsSource 属性?
<TabControl Grid.Row="1" Grid.Column="2" TabStripPlacement="Bottom" >
<TabControl.ContentTemplate>
<DataTemplate DataType="viewModel:ListViewModel" >
<view:ListView />
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemsSource>
<Binding Path="Lists"/>
</TabControl.ItemsSource>
</TabControl>
答案 0 :(得分:2)
没有简单的方法可以做到这一点。
问题是你有一个WPF模板,无论你背后放什么数据,它都是一样的。因此,创建了模板的一个副本,并且只要WPF在您的UI树中遇到ListViewModel
,它就会使用该模板绘制它。未绑定到DataContext的该控件的属性将在更改DataSource之间保持其状态。
您可以使用x:Shared="False"
(示例here),但这会在WPF请求时创建模板的新副本,包括切换标签时的内容。
当[
x:Shared
]设置为false时,修改Windows Presentation Foundation(WPF)资源检索行为,以便对资源的请求将为每个请求创建新实例,而不是为所有请求共享同一实例
您真正需要的是TabControl.Items
为每个项目生成控件的新副本,但是当您使用ItemsSource
属性时这不会发生(这是设计使然)
可能有效的替代方法是创建一个绑定到项集合的自定义DependencyProperty,并为集合中的每个项生成TabItem
和UserControl
个对象。此自定义DP还需要处理集合更改事件,以确保TabItem与您的集合保持同步。
这是我正在玩的那个。它适用于简单的情况,例如绑定到ObservableCollection,以及添加/删除项目。
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);
}
}
}
它从XAML中使用如下:
<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
答案 1 :(得分:1)
基于@Rachel的答案,我进行了一些修改。
首先,您现在必须将用户控件类型指定为动态创建的内容模板。
我还纠正了collectionChanged处理程序删除中的一个错误。
代码如下:
public static class TabControlExtension
{
// Custom DependencyProperty for a CachedItemsSource
public static readonly DependencyProperty CachedItemsSourceProperty =
DependencyProperty.RegisterAttached("CachedItemsSource", typeof(IList), typeof(TabControlExtension), new PropertyMetadata(null, CachedItemsSource_Changed));
// Custom DependencyProperty for a ItemsContentTemplate
public static readonly DependencyProperty ItemsContentTemplateProperty =
DependencyProperty.RegisterAttached("ItemsContentTemplate", typeof(Type), typeof(TabControlExtension), new PropertyMetadata(null, CachedItemsSource_Changed));
// Get items
public static IList GetCachedItemsSource(DependencyObject dependencyObject)
{
if (dependencyObject == null)
return null;
return dependencyObject.GetValue(CachedItemsSourceProperty) as IList;
}
// Set items
public static void SetCachedItemsSource(DependencyObject dependencyObject, IEnumerable value)
{
if (dependencyObject != null)
dependencyObject.SetValue(CachedItemsSourceProperty, value);
}
// Get ItemsContentTemplate
public static Type GetItemsContentTemplate(DependencyObject dependencyObject)
{
if (dependencyObject == null)
return null;
return dependencyObject.GetValue(ItemsContentTemplateProperty) as Type;
}
// Set ItemsContentTemplate
public static void SetItemsContentTemplate(DependencyObject dependencyObject, IEnumerable value)
{
if (dependencyObject != null)
dependencyObject.SetValue(ItemsContentTemplateProperty, value);
}
// Change Event
public static void CachedItemsSource_Changed(
DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (!(dependencyObject is TabControl))
return;
var changeAction = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
if (dependencyObject is TabControl tabControl && GetItemsContentTemplate(tabControl) != null && GetCachedItemsSource(tabControl) != null)
UpdateTabItems(tabControl);
});
// if the bound property is an ObservableCollection, attach change events
if (e.OldValue is INotifyCollectionChanged oldValue)
oldValue.CollectionChanged -= changeAction;
if (e.NewValue is INotifyCollectionChanged newValue)
newValue.CollectionChanged += changeAction;
if (GetItemsContentTemplate(dependencyObject) != null && GetCachedItemsSource(dependencyObject) != null)
UpdateTabItems(dependencyObject as TabControl);
}
private static void UpdateTabItems(TabControl tabControl)
{
if (tabControl == null)
return;
IList itemsSource = GetCachedItemsSource(tabControl);
if (itemsSource == null || itemsSource.Count == 0)
{
if (tabControl.Items.Count > 0)
tabControl.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 (tabControl.Items.Count <= i)
{
TabItem tabItem = new TabItem
{
DataContext = itemsSource[i],
Content = Activator.CreateInstance(GetItemsContentTemplate(tabControl))
};
tabControl.Items.Add(tabItem);
continue;
}
TabItem current = tabControl.Items[i] as TabItem;
if (!(tabControl.Items[i] is TabItem))
continue;
if (current.DataContext == itemsSource[i])
continue;
current.DataContext = itemsSource[i];
}
// loop backwards and cleanup extra tabs
for (int i = tabControl.Items.Count; i > itemsSource.Count; i--)
{
tabControl.Items.RemoveAt(i - 1);
}
}
}
此方法通过以下方式使用:
<TabControl main:TabControlExtension.CachedItemsSource="{Binding Channels}" main:TabControlExtension.ItemsContentTemplate="{x:Type YOURUSERCONTROLTYPE}">
<TabControl.Resources>
<Style BasedOn="{StaticResource {x:Type TabItem}}" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.Resources>
</TabControl>