在 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 信息)项目创建 ListViewItem
。 ListView::ContainerFromIndex
和 ListView::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 到模型设计的集成并不理想。
我正在使用来自 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 信息)项目创建 ListViewItem
。 ListView::ContainerFromIndex
和 ListView::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 到模型设计的集成并不理想。