如何正确的使用ListView

这篇文章给大家介绍如何正确的使用ListView,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

一、获取、设置 ListView 的滚动位置

如何正确的使用ListView

关于获取、设置 ListView 的滚动位置,微软已经提供了相关的例子,我在这个 Demo 中是直接套用的。这个功能主要是通过 ListViewPersistenceHelper 来实现的,它提供以下两个方法:

//获取ListView的滚动位置
publicstaticstringGetRelativeScrollPosition(ListViewBaselistViewBase,ListViewItemToKeyHandleritemToKeyHandler)

//设置ListView的滚动位置
publicstaticIAsyncActionSetRelativeScrollPositionAsync(ListViewBaselistViewBase,StringrelativeScrollPosition,ListViewKeyToItemHandlerkeyToItemHandler)

这两个方法中各有一个参考是委托类型,分别是ListViewItemToKeyHandler 和 ListViewKeyToItemHandler,它们的作用是告诉这个类如何处理列表项与 Key 的对应关系,好使得该类可以正确地获取或设置滚动位置。这里的 Key 是 ListViewItem 所代表的项目的一个属性(比如 Demo 中 Item 类的 Id 属性),这个属性的值在整个列表中是唯一的;而 Item 是在 Item 对象本身。在 Demo 中它们的实现分别如下:

privatestringItemToKeyHandler(objectitem)
{
ItemdataItem=itemasItem;
if(dataItem==null)returnnull;

returndataItem.Id.ToString();
}

privateIAsyncOperation<object>KeyToItemHandler(stringkey)
{
Func<System.Threading.CancellationToken,Task<object>>taskProvider=token=>
{
varitems=listView.ItemsSourceasList<Item>;
if(items!=null)
{
vartargetItem=items.FirstOrDefault(m=>m.Id==int.Parse(key));
returnTask.FromResult((object)targetItem);
}
else
{
returnTask.FromResult((object)null);
}
};
returnAsyncInfo.Run(taskProvider);
}

实现这两个方法后,重载列表页的 OnNavigatingFrom 方法,在其中加入以下代码,来实现获取滚动位置并保存:

stringposition=ListViewPersistenceHelper.GetRelativeScrollPosition(this.listView,ItemToKeyHandler);
NavigationInfoHelper.SetInfo(targetItem,position);

继续为页面注册 Loaded 事件,在 Loaded 事件中加入以下代码来实现设置滚动位置:

if(navigationParameter!=null)
{
if(NavigationInfoHelper.IsHasInfo)
{
awaitListViewPersistenceHelper.SetRelativeScrollPositionAsync(listView,NavigationInfoHelper.LastPosition,KeyToItemHandler);
}
}

这里需要注意的是,设置滚动位置的方法是异步的,所以 Loaded 方法需要加上 async 修饰符。而上述代码中对 navigationParameter 参数的判断则是为了区别:在导航时是否定位到最近浏览的位置,具体可参考 Demo 的代码。

二、获取 ListView 滚动位置处的项目

关于第二个需求的实现,我们首先需要明白以下三点:

  • ListView 的模板 (Template) 中包括 ScrollViewer,我们可以通过 VisualTreeHelper 获取到此控件;

  • ListView 提供 ContainerFromItem 方法,它使们可以通过传递 Item 获取包括此 Item 的 Container,即 ListViewItem;

  • UIElement 提供 TransformToVisual 方法,可以得到某控件相对指定控件的位置转换信息;

所以我们的思路就是:得到 ListView 控件中的 ScrollViewer,并遍历 ListView 中所有的 Item,在遍历过程中,得到每一项目的 ListViewItem,并判断它的位置是否位于 ScrollViewer 的位置中。以下是获取 ListView 中当前所有可见项的代码:

publicstaticList<T>GetAllVisibleItems<T>(thisListViewBaselistView)
{
varscrollViewer=listView.GetScrollViewer();
if(scrollViewer==null)
{
returnnull;
}

List<T>targetItems=newList<T>();
foreach(TiteminlistView.Items)
{
varitemContainer=listView.ContainerFromItem(item)asFrameworkElement;
boolisVisible=IsVisibileToUser(itemContainer,scrollViewer,true);
if(isVisible)
{
targetItems.Add(item);
}
}

returntargetItems;
}

在上述代码的 foreach 循环中的部分,正是我们前述思路的体现。而其中所调用的 IsVisibleToUser 方法,则是如何判断某一 ListViewItem 是否在 ScrollViewer 中为当前可见。其代码如下:

///<summary>
///Codefromhere:
///https://social.msdn.microsoft.com/Forums/en-US/86ccf7a1-5481-4a59-9db2-34ebc760058a/uwphow-to-get-the-first-visible-group-key-in-the-grouped-listview?forum=wpdevelop
///</summary>
///<paramname="element">ListViewItemorelementinListViewItem</param>
///<paramname="container">ScrollViewer</param>
///<paramname="isTotallyVisible">Iftheelementispartiallyvisible,thenincludeit.Thedefaultvalueisfalse</param>
///<returns>Getthevisibilityofthetargetelement</returns>
privatestaticboolIsVisibileToUser(FrameworkElementelement,FrameworkElementcontainer,boolisTotallyVisible=false)
{
if(element==null||container==null)
returnfalse;

if(element.Visibility!=Visibility.Visible)
returnfalse;

RectelementBounds=element.TransformToVisual(container).TransformBounds(newRect(0.0,0.0,element.ActualWidth,element.ActualHeight));
RectcontainerBounds=newRect(0.0,0.0,container.ActualWidth,container.ActualHeight);

if(!isTotallyVisible)
{
return(elementBounds.Top<containerBounds.Bottom&&elementBounds.Bottom>containerBounds.Top);
}
else
{
return(elementBounds.Bottom<containerBounds.Bottom&&elementBounds.Top>containerBounds.Top);
}
}

可以看出,我们是能过得到两个 Rect 值。Rect 类型的值代表一个矩形区域的位置和大小,我们对这两个值进行比较后,返回最终的结果。

获取 ListViewItem 的 Rect 值: element.TransformToVisual(container) 返回的结果是 GeneralTransform 类型,这个值表明了 ListViewItem 相对于 Container(即 ScrollViewer)的位置转换信息。GeneralTransform 类型可能我们并不太熟悉,不过,从它派生出来的这些类: ScaleTransform、TranslateTransform ,我们就熟悉了,GeneralTransform 正是它们的基类。GeneralTransform 包括以下两个重要的方法:

  • TransformPoint, 可以将得到的转换信息计算成 Point 值,表示某控件相对于另一控件的坐标位置

  • TransformBounds,可以将得到的转换信息计算成 Rect 值,表示某控件相对于另一控件的坐标位置及所占的区域。

所以,我们通过 TransformBounds 方法就得到了 ListViewItem 相对于 ScrollViewer 的位置和所占区域的信息。

获取 ScrollViewer 的 Rect 值: 直接实例化一个 Rect,以 0,0 作为你左上角的坐标位置点, ScrollViewer 的 ActualWidth 和 ActualHeight 作为其大小。

接下来,就是比较的过程:这里,我们做了一个判断,判断是否要求元素 (ListViewItem) 完全在 ScrollViewer 中(而非仅部分在其中)。如果要求部分显示即可,则只要元素的 Top 小于 Container 的 Bottom 值,并且元素的 Bottom 大于 Container 的 Top;如果要求全部显示,那么算法是:元素的 Top 大于 Container 的 Top 并且元素的 Bottom 小于 Container 的 Bottom。如果您对语言描述或者代码都还不明白,也可以在纸上画一下进行比较。

接下来,我们照着 GetAllVisbleItems 方法的思路可以实现 GetFirstVisibleItem 方法,即获取列表中第一个可见项,代码可参考 Demo 的源码,在此不再赘述。

我们在之前重载的方法 OnNavigatingFrom 中加上这句代码,即可以获取到用户浏览位置处的那一项。

vartargetItem=this.listView.GetFirstVisibleItem<Item>();

关于如何正确的使用ListView就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

发布于 2021-04-03 22:31:28
收藏
分享
海报
0 条评论
170
上一篇:JWPlayer插件怎么在jQuery中使用 下一篇:CONST怎么在C/C++中使用
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码