在 ItemsControl 中插入一个项目
Inserting an item in an ItemsControl
这是要求。
在我的ItemsControl
中(你可以使用ListView
如果它有助于你考虑场景)。我想注入的不是记录,而是一个唯一的 DataTemplate 到我列表中的任意索引位置。
例如,我可能想将它插入第一个位置,索引 0,或第三个位置,索引 2,或者甚至可能有将它插入最后一个位置,索引 count-1 的逻辑。
我需要子class ListView 来完成这个,我意识到。也就是说,我可以轻松地创建 SpecialItemTemplate、SpecialItemIndex DP 属性以具有这些值。
新增要求:
- 不需要特殊类型的集合
- 不需要操作现有数据
- 也允许 IsHitTestVisible 可变
知道如何(在 WinRT 中)完成这一壮举吗?
这是一个基本上是 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();
}
这是要求。
在我的ItemsControl
中(你可以使用ListView
如果它有助于你考虑场景)。我想注入的不是记录,而是一个唯一的 DataTemplate 到我列表中的任意索引位置。
例如,我可能想将它插入第一个位置,索引 0,或第三个位置,索引 2,或者甚至可能有将它插入最后一个位置,索引 count-1 的逻辑。
我需要子class ListView 来完成这个,我意识到。也就是说,我可以轻松地创建 SpecialItemTemplate、SpecialItemIndex DP 属性以具有这些值。
新增要求:
- 不需要特殊类型的集合
- 不需要操作现有数据
- 也允许 IsHitTestVisible 可变
知道如何(在 WinRT 中)完成这一壮举吗?
这是一个基本上是 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();
}