如何更改在 ListView 中用作 ItemTemplate 的 UserControl 的 VisualState

How to change VisualState of UserControl used as ItemTemplate in ListView

我有一个 UserControl,其中定义了一些 VisualStates

<UserControl>
  <Grid Height="50" HorizontalAlignment="Stretch">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="SelectionStates">
            <VisualState x:Name="Unselected"/>
            <VisualState x:Name="Selected">
            <!-- more code -->

我将它用作 ListViewItemTemplate。选择 ListView 中的项目时,我还想更改 DataTemplateVisualState。现在我有这样一个方法:

private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  ListView listView = sender as ListView;
  foreach (var item in e.AddedItems)
    VisualStateManager.GoToState((listView.ContainerFromItem(item) as ListViewItem).ContentTemplateRoot as myControl, "Selected", false);
 }

它似乎有效,但在某些情况下 (listView.ContainerFromItem(item) as ListViewItemnull,因此抛出异常。

是否有任何other/better方法来改变使用的DataTemplateVisualState

项目容器(即 ListViewItem)是 null 的原因是因为 ListView 的默认面板 ItemsStackPanel 是一个 像素-based UI 虚拟化 面板,仅当项目 在或接近当前视口 .

时才呈现项目

由于您在代码中设置了选定项目,这些项目可能不会呈现,它们的容器将 return 为 null

一个简单的修复方法是将 ItemsStackPanel 替换为正常的 StackPanel,但是这样做会破坏内置虚拟化。

这是不修改面板的另一种解决方案。

首先你现有的代码仍然需要,你只需要做一个空检查,如果项目容器是空的,什么都不做。

然后,您需要订阅 ListViewContainerContentChanging 事件。当项目容器被加载时会触发。所以基本上,您检查此处加载的项目是否是所选项目之一。如果是,则更改其视觉状态。像这样 -

private void MyListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    if (this.MyListView.SelectedItems.Contains(args.Item))
    {
        // get the container
        var container = (ListViewItem)args.ItemContainer; 

        // do your visual state change here, when the container was previously null
    }
}