【问题标题】:Inserting an item in an ItemsControl在 ItemsControl 中插入项目
【发布时间】:2015-10-16 18:48:19
【问题描述】:

这是要求。

在我的ItemsControl 中(如果可以帮助您考虑场景,您可以使用ListView)。我想将唯一的 DataTemplate 注入到列表中的任意索引位置,而不是记录。

例如,我可能想将它插入到第一个位置,索引 0,或第三个位置,索引 2,或者甚至可能有逻辑将它插入到最后一个位置,索引 count-1。

我意识到,我需要子类 ListView 来完成这个。话虽如此,我可以轻松地创建 SpecialItemTemplate、SpecialItemIndex DP 属性来获取这些值。

添加要求:

  1. 不需要特殊类型的集合
  2. 不需要处理现有数据
  3. 也允许 IsHitTestVisible 可变

任何想法如何完成这一壮举(在 WinRT 中)?

【问题讨论】:

  • 你在这个场景中做虚拟化 Jerry 吗? ....你只需要选择一个星期五来问。 :P
  • 我认为这与虚拟化不太兼容。所以,为了简单起见,让我们说不。有办法做到这一点,对吧?我只知道有。
  • 克里斯,您以一种真实的方式解决了这个问题,而不是一种破解方式,我会寄给您一台 Lumia 820 和一件 Microsoft 衬衫作为感谢。不开玩笑。怎么会毁了你的周末?
  • 如何使用适配器生成第二个列表并绑定到 ListView? WinRT 甚至支持隐式模板吗?
  • 嗯,我试了 10 分钟,但事实证明 RT 部分很有趣,但到目前为止还没有结果。除了这个周末是婚礼、生日、家务和皇家队比赛,我已经有了 820 和一件衬衫,但如果时间不只是为了公开挑战,我会回来看看我们是否能解决一些问题。 :)

标签: wpf xaml silverlight winrt-xaml itemscontrol


【解决方案1】:

这是一个基本上是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();
}

【讨论】:

  • 这需要一些时间来解析。如果数据源不可观察,它是否有效?它会覆盖记录还是将其推出?
  • @JerryNixon-MSFT:它适用于任何数据源,除了关联对象必须是 ItemsControl 之外,没有关于环境的假设。不,它不会覆盖/推出任何记录,实际上所有记录都没有改变。它搜索项目的视觉表示(称为itemcontainer)并更改其Template,仅此而已(它甚至稍后恢复旧模板)
  • 啊,是的。老实说,我认为这可能是唯一可行的方法。因此,如果我有 5 条记录显示并且我以这种方式将您的行为注入其中,那么我只有 4 条记录显示加上自定义模板,对吗?
  • 我突然意识到我可能误解了你想要完成的事情。我的自定义行为旨在使用 itemTemplate 的替代版本显示集合的任意记录,但它仍然意味着显示为记录。现在我开始认为这不是你所需要的,你想偷偷溜进一些东西(比如“以下项目不太重要”的消息)。好吧,如果是这种情况,我会推荐一种完全不同的方法。如果是这样,请告诉我,我会尽力为你起草我的想法。
猜你喜欢
  • 2013-06-03
  • 1970-01-01
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
相关资源
最近更新 更多