這是一個解決方案,基本上是一個Behavior
與Template
屬性,它可以連接到任何ItemsControl
。我使用虛擬化和非虛擬化面板對其進行了測試,適用於這兩種情況。如果你認爲代碼是錯綜複雜的......好吧,我不能不同意,寫了一段時間,我不記得我有什麼理由按它的方式寫它。
用法:
<ListBox ItemsSource="{Binding Persons}">
<Interactivity:Interaction.Behaviors>
<AlternativeItemTemplate Index="42">
<DataTemplate>
...foo...
</DataTemplate>
</AlternativeItemTemplate>
</Interactivity:Interaction.Behaviors>
<ListBox.ItemTemplate>
<DataTemplate>
...bar...
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
和類:
[ContentProperty("ItemTemplate")]
public class AlternativeItemTemplate : ItemContainerDecorator
{
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(AlternativeItemTemplate), new PropertyMetadata(null));
public int Index
{
get { return (int) GetValue(IndexProperty); }
set { SetValue(IndexProperty, value); }
}
public static readonly DependencyProperty IndexProperty =
DependencyProperty.Register("Index", typeof(int), typeof(AlternativeItemTemplate), new PropertyMetadata(-1));
protected override void OnContainersChanged()
{
if (!AssociatedObject.Items.Any() || Index < 0 || Index >= AssociatedObject.Items.Count)
{
ItemContentPresenter = null;
ItemContentControl = null;
m_overwrittenTemplate = null;
return;
}
TryUpdateItem(ItemContainerGenerator.ContainerFromItem(AssociatedObject.Items[Index]));
}
private ContentPresenter ItemContentPresenter { get; set; }
private ContentControl ItemContentControl { get; set; }
private DataTemplate m_overwrittenTemplate;
private void TryUpdateItem(DependencyObject itemContainer)
{
if (itemContainer == null)
{
ResetOverwrittenTemplate();
}
var containerAsPresenter = itemContainer as ContentPresenter;
if (containerAsPresenter != null) UpdateItemContentPresenter(containerAsPresenter);
else
{
var containerAsControl = itemContainer as ContentControl;
if (containerAsControl != null) UpdateItemContentControl(containerAsControl);
}
}
private void ResetOverwrittenTemplate()
{
if (ItemContentPresenter != null)
ItemContentPresenter.ContentTemplate = m_overwrittenTemplate;
if (ItemContentControl != null)
ItemContentControl.ContentTemplate = m_overwrittenTemplate;
ItemContentPresenter = null;
ItemContentControl = null;
m_overwrittenTemplate = null;
}
private void UpdateItemContentPresenter(ContentPresenter container)
{
if (ItemContentPresenter != null)
ItemContentPresenter.ContentTemplate = m_overwrittenTemplate;
ItemContentPresenter = container;
m_overwrittenTemplate = ItemContentPresenter.ContentTemplate;
ItemContentPresenter.ContentTemplate = ItemTemplate;
}
private void UpdateItemContentControl(ContentControl container)
{
if (ItemContentControl != null)
ItemContentControl.ContentTemplate = m_overwrittenTemplate;
ItemContentControl = container;
m_overwrittenTemplate = ItemContentControl.ContentTemplate;
ItemContentControl.ContentTemplate = ItemTemplate;
}
}
public abstract class ItemContainerDecorator : Behavior<ItemsControl>
{
private Dictionary<object, DependencyObject> LastKnownContainers = new Dictionary<object, DependencyObject>();
protected ItemContainerGenerator ItemContainerGenerator { get { return (AssociatedObject != null) ? AssociatedObject.ItemContainerGenerator : null; } }
protected override void OnAttached()
{
base.OnAttached();
ItemContainerGenerator.ItemsChanged += HandleItemsChangedInitially;
if (!TryAddObservers())
{
AssociatedObject.Loaded += AddObserversOnLoaded;
}
AssociatedObject.Loaded += OnItemsControlLoaded;
AssociatedObject.LayoutUpdated += OnItemsControlLayoutUpdated;
CheckContainersChanged();
}
private void OnItemsControlLayoutUpdated(object sender, EventArgs eventArgs)
{
CheckContainersChanged();
}
private void OnItemsControlLoaded(object sender, RoutedEventArgs e)
{
CheckContainersChanged();
}
private void AddObserversOnLoaded(object sender, RoutedEventArgs e)
{
AssociatedObject.Loaded -= AddObserversOnLoaded;
TryAddObservers();
}
private bool TryAddObservers()
{
const bool success = true;
Panel itemsHost =
AssociatedObject.GetVisualDescendants().OfType<Panel>().FirstOrDefault(panel => panel.IsItemsHost);
if (itemsHost != null)
{
var virtualizingItemsHost = itemsHost as VirtualizingPanel;
if (virtualizingItemsHost != null)
{
virtualizingItemsHost.LayoutUpdated += OnVirtualizingItemsHostLayoutUpdated;
m_virtualizingItemsHost = virtualizingItemsHost;
}
return success;
}
return !success;
}
private VirtualizingPanel m_virtualizingItemsHost;
private bool LayoutUpdatedOccurredFirst;
private void OnVirtualizingItemsHostLayoutUpdated(object sender, EventArgs eventArgs)
{
LayoutUpdatedOccurredFirst = true;
CheckContainersChanged();
}
protected override void OnDetaching()
{
ItemContainerGenerator.ItemsChanged -= HandleItemsChangedInitially;
ItemContainerGenerator.ItemsChanged -= HandleItemsChanged;
AssociatedObject.Loaded -= OnItemsControlLoaded;
AssociatedObject.LayoutUpdated -= OnItemsControlLayoutUpdated;
AssociatedObject.Loaded -= AddObserversOnLoaded;
if (m_virtualizingItemsHost != null) m_virtualizingItemsHost.LayoutUpdated -= OnVirtualizingItemsHostLayoutUpdated;
m_virtualizingItemsHost = null;
base.OnDetaching();
}
private void HandleItemsChangedInitially(object sender, ItemsChangedEventArgs e)
{
ItemContainerGenerator.ItemsChanged -= HandleItemsChangedInitially;
if (!LayoutUpdatedOccurredFirst)
{
//sometimes calling UpdateLayout throws an ArgumentException
//don't know why so we just swallow it
//it's not particularly important
try
{
AssociatedObject.UpdateLayout();
}
catch (ArgumentException) { }
}
ItemContainerGenerator.ItemsChanged += HandleItemsChanged;
CheckContainersChanged();
}
private void HandleItemsChanged(object sender, ItemsChangedEventArgs e)
{
CheckContainersChanged();
}
private void CheckContainersChanged()
{
var newestContainers = new Dictionary<object, DependencyObject>();
foreach (var item in AssociatedObject.Items)
{
newestContainers[item] = ItemContainerGenerator.ContainerFromItem(item);
}
if (!LastKnownContainers.SequenceEqual(newestContainers))
{
LastKnownContainers = newestContainers;
OnContainersChanged();
}
}
protected abstract void OnContainersChanged();
}
你在這種情況下傑裏在做虛擬化? ....你只需要選擇一個星期五問。 :P –
我認爲這與虛擬化不太相稱。所以,要保持簡單,讓我們說不。有辦法做到這一點,對吧?我只知道有。 –
克里斯你真正的解決這個問題,而不是一種破解方式,我會送你一個Lumia 820和一件微軟襯衫作爲謝謝。沒有開玩笑。那如何毀了你的週末? –