Reputation: 31813
Here are the requirements.
In my ItemsControl
(you can use a ListView
if it helps you to consider the scenario). I want to inject, not a record, but a unique DataTemplate into an arbitrary indexed location in my list.
For example, I might want to insert it into the first position, index 0, or the third position, index 2, or perhaps even have the logic to insert it into the last position, index count-1.
I will need to sub-class ListView to accomplish this, I realize. That being said, I could easily create the SpecialItemTemplate, SpecialItemIndex DP properties to have the values.
Added requirements:
Any ideas how to accomplish this feat (in WinRT)?
Upvotes: 1
Views: 232
Reputation: 6146
Here is a solution that is basically a Behavior
with a Template
property which can be attached to any ItemsControl
. I tested it with virtualizing and non-virtualizing panels, works for both cases. If you think the code is convoluted... well I can't disagree, wrote it a while back and I can't remember what reasons I had to write it the way it ended up.
Usage:
<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>
and the classes:
[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();
}
Upvotes: 1