【发布时间】:2010-08-16 14:46:48
【问题描述】:
我有一个实体类。这个实体有很多属性,实体的数据以TabControl 的几个TabItems 显示给用户。我还实现了 MVVM 方法。
当屏幕首先显示给用户时,我只想绑定活动的标签页控件,并且当用户在标签页中导航时,将根据需要进行额外的单独绑定。我怎样才能做到这一点?
【问题讨论】:
标签: wpf mvvm binding lazy-loading tabcontrol
我有一个实体类。这个实体有很多属性,实体的数据以TabControl 的几个TabItems 显示给用户。我还实现了 MVVM 方法。
当屏幕首先显示给用户时,我只想绑定活动的标签页控件,并且当用户在标签页中导航时,将根据需要进行额外的单独绑定。我怎样才能做到这一点?
【问题讨论】:
标签: wpf mvvm binding lazy-loading tabcontrol
您没有任何事情可做,这是默认行为。在选择此 TabItem 之前,不会实例化 TabItem 内容的 DataTemplate
编辑:这是一个例子:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:Page1ViewModel}">
<v:Page1View />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Page3ViewModel}">
<v:Page3View />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Page3ViewModel}">
<v:Page3View />
</DataTemplate>
</Window.Resources>
...
<TabControl ItemsSource="{Binding Pages}"
DisplayMemberPath="Title">
</TabControl>
在上面的代码中,TabControl 将根据项目类型选择适当的 DataTemplate,并仅在选择该项目时呈现它。
编辑 2:显然您想在多个页面上显示单个 ViewModel 的数据。如果你想让每个TabItem的控件延迟实例化,你需要使用每个TabItem的ContentTemplate属性:
<TabControl>
<TabItem Header="Page 1">
<TabItem.ContentTemplate>
<DataTemplate>
<v:Page1View />
</DataTemplate>
</TabItem.ContentTemplate>
</TabItem>
<TabItem Header="Page 2">
<TabItem.ContentTemplate>
<DataTemplate>
<v:Page2View />
</DataTemplate>
</TabItem.ContentTemplate>
</TabItem>
<TabItem Header="Page 3">
<TabItem.ContentTemplate>
<DataTemplate>
<v:Page3View />
</DataTemplate>
</TabItem.ContentTemplate>
</TabItem>
</TabControl>
【讨论】:
我创建了这个适用于我们第三方 tabcontrol 的解决方案。
这个想法是在设置数据上下文时“拦截”数据上下文,将其保存以备后用,并将数据上下文设置回空。当 tabitem 获得焦点时,我们设置 datacontext 并且数据将填充到选项卡中。
作为依赖属性实现。然后只需在需要的选项卡上设置属性(不要在默认出现的选项卡上设置)
#region SavedDataContext
private static object GetSavedDataContext(TabItemEx tabItem)
{
return tabItem.GetValue(SavedDataContextProperty);
}
private static void SetSavedDataContext(TabItemEx tabItem, object value)
{
tabItem.SetValue(SavedDataContextProperty, value);
}
public static readonly DependencyProperty SavedDataContextProperty =
DependencyProperty.RegisterAttached("SavedDataContext", typeof(object),
typeof(Attach), new UIPropertyMetadata(null));
#endregion
#region LazyLoad
public static bool GetLazyLoad(TabItemEx tabItem)
{
return (bool)tabItem.GetValue(LazyLoadProperty);
}
public static void SetLazyLoad(TabItemEx tabItem, bool value)
{
tabItem.SetValue(LazyLoadProperty, value);
}
private static readonly DependencyProperty LazyLoadProperty =
DependencyProperty.RegisterAttached("LazyLoad", typeof(bool),
typeof(Attach), new UIPropertyMetadata(false, LazyLoadPropertyChanged));
private static void LazyLoadPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs eventArgs)
{
if ((bool)eventArgs.NewValue)
{
var tabItemEx = sender as TabItemEx;
if (tabItemEx == null)
return;
tabItemEx.DataContextChanged += DataContextChanged;
tabItemEx.GotFocus += TabGotFocus;
}
}
#endregion
private static void TabGotFocus(object sender, RoutedEventArgs e)
{
var tabItemEx = sender as TabItemEx;
if (tabItemEx == null)
return;
tabItemEx.GotFocus -= TabGotFocus;
tabItemEx.DataContext = GetSavedDataContext(tabItemEx);
tabItemEx.IsSelected = true;
}
private static void DataContextChanged(object sender, DependencyPropertyChangedEventArgs eventArgs)
{
var tabItemEx = sender as TabItemEx;
if (tabItemEx == null)
return;
SetSavedDataContext(tabItemEx, eventArgs.NewValue);
tabItemEx.DataContextChanged -= DataContextChanged;
tabItemEx.DataContext = null;
}
【讨论】:
标记为答案的方法有一个缺点 - TabItem 的内容在选择时总是会重新呈现。如果它很关键 - 你可以试试this。
【讨论】: