TabControl 的 ItemsSource 属性绑定(bind)到 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>
最佳答案
没有简单的方法可以做到这一点。
问题是您有一个 WPF 模板,无论您在其后面放置什么数据,该模板都是相同的。因此,会创建模板的一份副本,并且每当 WPF 在 UI 树中遇到 ListViewModel
时,它都会使用该模板来绘制它。未绑定(bind)到 DataContext 的控件属性将在更改 DataSource 之间保留其状态。
您可以使用 x:Shared="False"
(例如 here ),但这会在 WPF 请求时创建模板的新副本,包括您切换选项卡时。
When [
x:Shared
is] set to false, modifies Windows Presentation Foundation (WPF) resource retrieval behavior such that requests for a resource will create a new instance for each request, rather than sharing the same instance for all requests.
您真正需要的是 TabControl.Items
为每个项目生成一个新的控件副本,但当您使用 ItemsSource
时,这种情况不会发生> 属性(这是设计使然)。
一种可行的替代方案是创建一个自定义 DependencyProperty,它绑定(bind)到您的项目集合,并为项目中的每个项目生成 TabItem
和 UserControl
对象。收藏。此自定义 DP 还需要处理集合更改事件,以确保 TabItem 与您的集合保持同步。
这是我正在玩的一个。它适用于简单的情况,例如绑定(bind)到 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
中为其设置绑定(bind)- DependencyProperty 实现当前对新 UserControl 的创建进行硬编码。可能想以其他方式做到这一点,例如尝试使用模板属性或可能使用不同的 DP 来告诉它要创建什么 UserControl
- 可能需要更多测试...不确定是否存在由于更改处理程序等导致的内存泄漏问题
关于Wpf TabControl 在所有选项卡上仅创建一个 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43347266/