Force redraw on ListView in windows store app
Asked Answered
C

1

0

I am using @Romasz 's answer from Alternating Colors of rows in ListView in Windows Phone 8.1 to give alternate background to ListView items. I change it in order to highlight the selected item, like this:

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

Note: I removed the AlternateBrushes property as I only need static colors and add in HighlightItem property binding to the list's selected index.

Then I changed the converter class AlternateConverter appropriately as follow: [NB: this is C++ counterpart.]

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

The problem is, as you might guess from the title, the background for an item in the ListView is not re-rendered whenever I select it. In my experience, this triggers the fact that I did not handle event fired when the selection is made. So I added

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

Unfortunately, there is no place in the API telling me how to properly redraw the ListView. The closest thing I could get is by this stupid code:

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

Now, the new problem is that the list view flickers after every selection and the viewing state of the list is totally lost (e.g. the list automatically scrolls back to the beginning of the list).

So I want to ask for the proper way to force a ListView (or any other UI element) to redraw itself.

Crinose answered 20/2, 2015 at 5:0 Comment(1)
I've got two fast ideas: 1. define a property in your item which wil be responsible for background color, it can be even bool with spropriate converter, 2. upon selectionchanged don't change itemtemplate, but change only the color of deselected item - I think this can also be done in xaml.Mickey
C
0

It turns out that ListView create ListViewItem using the supplied data template for each "logical" (non-UI information) item. The methods ListView::ContainerFromIndex and ListView::ContainerFromItem allows one to access the ListViewItem from a given index or from the logical item. Then we can manipulate the property ListViewItem::ContentTemplateRoot to achieve what we need.

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

This solution is not perfect. If one selects an item and scroll long down the list so that the selected item is out of view and then select a new item, the background of the unselected is not restored to normal state, so when one nagivates upward, the item appears selected!?

The best i.e. fully working solution comes from Romasz's comment though such integration-of-UI-into-model design is less than ideal.

Crinose answered 11/3, 2015 at 3:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.