问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/28621914/force-redraw-on-listview-in-windows-store-app