UWP 应用中的 GetContainerForItemOverride

GetContainerForItemOverride in UWP app

我正在开发一个应用程序,该应用程序使用跨平台第三方布局引擎将 UI elements 放置在 Visual Tree 中的特定坐标(通过将内容放入画布中)。在我的场景中,我需要一个虚拟化 ListView,它的每一项都通过这个布局引擎。

一切正常,直到我尝试从列表视图中删除项目。我已将问题缩小到这样一个事实,即对于我的列表视图,我在调用 GetContainerForItemOverride() 时返回 Canvas,而不是返回 ListViewItem。但是我当然需要一个 Canvas 以便布局引擎可以将内容放在 Canvas.

中的特定坐标处

我在下面创建了一个非常愚蠢的示例,它将重现我遇到的问题。基本上,当我尝试通过调用 .RemoveAt().Remove() 删除一个项目时,我会得到一个 InvalidCastException (顺便说一句,如果我在这个例子中使用 ItemsSource而不是直接添加到 .Items).

有人知道如何解决这个问题吗?

这是代码

<Page x:Class="Sample.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sample="using:Sample">

    <StackPanel>
        <sample:CustomListViewCrash x:Name="MyListViewCrash">
            <sample:CustomListViewCrash.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </sample:CustomListViewCrash.ItemsPanel>
        </sample:CustomListViewCrash>

        <Button Content="Delete" Tapped="Delete_OnTapped" />
    </StackPanel>

</Page>

以及背后的代码

public sealed partial class MainPage
{
    public MainPage()
    {
        InitializeComponent();
        MyListViewCrash.Items.Add("blah blah");
    }

    private void Delete_OnTapped(object sender, TappedRoutedEventArgs e)
    {
        if (MyListViewCrash.Items.Count > 0)
        {
            MyListViewCrash.Items.RemoveAt(0);
        }

    }
}

public class CustomListViewCrash : ListView
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        var canvas = new Canvas
        {
            Width = 100,
            Height = 50
        };

        canvas.Children.Add(new Button());

        return canvas;
    }

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        var canvas = (Canvas) element;
        var button = (Button) canvas.Children[0];
        button.Content = item;

        base.PrepareContainerForItemOverride(element, item);
    }
}

这里是关于异常的信息:

System.InvalidCastException occurred
  HResult=0x80004002
  Message=Specified cast is not valid.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Runtime.InteropServices.WindowsRuntime.IVector`1.RemoveAt(UInt32 index)
   at System.Runtime.InteropServices.WindowsRuntime.VectorToListAdapter.RemoveAtHelper[T](IVector`1 _this, UInt32 index)
   at System.Runtime.InteropServices.WindowsRuntime.VectorToListAdapter.RemoveAt[T](Int32 index)
   at ReactiveDelete.MainPage.Delete_OnTapped(Object sender, TappedRoutedEventArgs e) in MainPage.xaml.cs:line 31

除了 "System.InvalidCastException: Specified cast is not valid" 之外,ListView 的容器应该是 ListViewItem

您应该能够设置 CustomListViewCrashItemsControl 继承来替换 ListView。当CustomListViewCrashclass继承自ItemsControl时,CustomListViewCrash的容器不是ListViewItem.

如果您希望 class 继承自 ListView,您应该能够将 Canvas 设置为 ListViewItemContent。我们应该能够删除 PrepareContainerForItemOverride.

中的 base.PrepareContainerForItemOverride(element, item) 方法

例如:

public class CustomListViewCrash : ListView
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        var canvas = new Canvas
        {
            Width = 100,
            Height = 50
        };
        canvas.Children.Add(new Button());
        var listViewItem = new ListViewItem();
        listViewItem.Content = canvas;
        return listViewItem;
    }

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        var listViewItem = (ListViewItem)element;
        var canvas = (Canvas)listViewItem.Content;
        var button = (Button)canvas.Children[0];
        button.Content = item;
    }
}