有沒有簡單的方法來做到這一點。
問題是你有一個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