在 windows 商店应用的 ListView 上强制重绘

Force redraw on ListView in windows store app

我正在使用来自 Alternating Colors of rows in ListView in Windows Phone 8.1 的 @Romasz 的回答来为 ListView 项目提供替代背景。我更改它以突出显示 selected 项目,如下所示:

<local:AlternateConverter CurrentList="{Binding ElementName=myList, Path=ItemsSource}" 
                          HighlightIndex="{Binding ElementName=myList, Path=SelectedIndex}"
                          x:Key="AlternateConverter"/>

注意:我删除了 AlternateBrushes 属性 因为我只需要静态颜色并添加 HighlightItem 属性 绑定到列表的 selected 索引.

然后我将转换器 class AlternateConverter 适当地更改如下:[注意:这是 C++ 的对应物。]

Object^ AlternateConverter::Convert(Object^ value, TypeName targetType, Object^ parameter, String^ language)
{
    auto list = reinterpret_cast<IVector<Object^>^>(CurrentList);
    unsigned int i;
    bool r = list->IndexOf(value, &i);

    // This is added to highlight the selected item with a different color
    if (i == HighlightIndex)
        return BrushHighlight;

    return i % 2 == 0 ? BrushEven : BrushOdd;
}

问题是,正如您可能从标题中猜到的那样,每当我 select 时,ListView 中某个项目的背景并不是 re-rendered。根据我的经验,这触发了一个事实,即我没有处理 selection 生成时触发的事件。所以我添加了

    <ListView x:Name="myList" SelectionChanged="OnItemSelected">
           <!-- Omitted code -->
    </ListView>

不幸的是,API 中没有地方告诉我如何正确 重绘 ListView。我能得到的最接近的东西是这个愚蠢的代码:

void MainPage::OnItemSelected(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
{
    // This is to trigger OnItemTemplateChanged which have a side effect of redrawing the whole list view.
    myList->ItemTemplate = myList->ItemTemplate;
}

现在,新问题是列表视图在每个 selection 后闪烁,并且列表的查看状态完全丢失(例如,列表自动滚动回列表的开头)。

所以我想问一下强制 ListView(或任何其他 UI 元素)重绘自身的正确方法。

事实证明,ListView 使用提供的数据模板为每个 "logical"(非 UI 信息)项目创建 ListViewItemListView::ContainerFromIndexListView::ContainerFromItem 方法允许从给定索引或逻辑项访问 ListViewItem。然后我们可以操纵属性ListViewItem::ContentTemplateRoot来实现我们需要的

void MainPage::OnSelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
{
    auto listview = (ListView^)sender;

    // Restore background for unselected item
    auto list = reinterpret_cast<IVector<Object^>^>(listview->ItemsSource);
    for (auto item : e->RemovedItems)
    {
        unsigned int index;
        list->IndexOf(item, &index);
        auto container = (ListViewItem^)listview->ContainerFromIndex(index);

        auto border = (Border^)container->ContentTemplateRoot;
        border->Background = index % 2 == 0 ? BrushEven : BrushOdd;

        // This null check is necessary because when the [unselected] item goes out of view, ListView seems to reuse the ListViewItem to display other item and simply return nullptr.
        if (container != nullptr)
        {
            auto border = (Border^)container->ContentTemplateRoot;
            border->Background = DefaultBrushTransparent;
        }
    }

    // Highlight the selected item
    for (auto item : e->AddedItems)
    {
        auto container = (ListViewItem^)listview->ContainerFromItem(item);
        auto border = (Border^)container->ContentTemplateRoot;
        border->Background = BrushHighlight;
    }
}

这个解决方案并不完美。如果一个 select 一个项目并向下滚动列表,使 select 编辑的项目不在视图中,然后 select 一个新项目,un[=24= 的背景]ed 没有恢复到正常状态,所以当向上导航时,该项目出现 selected!?

最好的即完全有效的解决方案来自 Romasz 的评论,尽管这种 UI 到模型设计的集成并不理想。