在Windows应用商店应用中强制重绘ListView

时间:2015-02-20 05:00:15

标签: listview windows-store-apps c++-cx

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

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

注意:我删除了AlternateBrushes属性,因为我只需要静态颜色,并将HighlightItem属性绑定添加到列表的所选索引中。

然后我按照以下方式适当更改了转换器类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;
}

问题是,正如您可能从标题中猜到的那样,每当我选择它时,ListView中的项目背景都不会重新呈现。根据我的经验,这会触发我在选择时没有处理事件的事实。所以我添加了

    <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;
}

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

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

1 个答案:

答案 0 :(得分:0)

事实证明ListView使用提供的数据模板为每个“逻辑”(非UI信息)项创建ListViewItem。方法ListView::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;
    }
}

这个解决方案并不完美。如果选择一个项目并在列表中向下滚动以使所选项目不在视图中然后选择一个新项目,则未选中的背景将不会恢复到正常状态,因此当一个向上nagivates时,该项目将显示为已选中! ?

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