重新想象 Windows 8.1 Store Apps (93) - 控件增强: GridView, ListView

时间:2021-06-13 12:25:57

[源码下载]

重新想象 Windows 8.1 Store Apps (93) - 控件增强: GridView, ListView

作者:webabcd

介绍
重新想象 Windows 8.1 Store Apps 之控件增强

  • GridView 和 ListView 每屏显示的数据量多滚动也流畅
  • GridViewItemPresenter 和 ListViewItemPresenter 更方便更快速地显示各种状态
  • 自定义 GridViewItemPresenter 和 ListViewItemPresenter

示例
1、演示 GridView 和 ListView 的新增特性: GridView 和 ListView 每屏显示的数据量多滚动也流畅
GridViewAndListView/Employee.cs

namespace Windows81.Controls.GridViewAndListView
{
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsMale { get; set; }
}
}

GridViewAndListView/TestData.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Windows81.Controls.GridViewAndListView
{
public class TestData
{
/// <summary>
/// 返回一个 Employee 数据集合,测试用
/// </summary>
public static List<Employee> GetEmployees()
{
var employees = new List<Employee>(); for (int i = ; i < ; i++)
{
employees.Add(
new Employee
{
Name = "Name " + i.ToString().PadLeft(, ''),
Age = new Random(i).Next(, ),
IsMale = Convert.ToBoolean(i % )
});
} return employees;
}
}
}

GridViewAndListView/IncrementalData.xaml

<Page
x:Name="pageRoot"
x:Class="Windows81.Controls.GridViewAndListView.IncrementalData"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows81.Controls.GridViewAndListView"
xmlns:common="using:Windows81.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Grid Background="Transparent"> <GridView x:Name="gridView" Margin="120 0 0 0"
ContainerContentChanging="gridView_ContainerContentChanging">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Height="100" Width="100" Background="Blue">
<Rectangle x:Name="placeholderRectangle" Fill="Red" Height="10" Opacity="0" />
<TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
<TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
<TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView> </Grid>
</Page>

GridViewAndListView/IncrementalData.xaml.cs

/*
* 演示 GridView 和 ListView 的新增特性
*
* 当 GridView 或 ListView 的一屏需要显示的数据量极大时(一屏的 item 多,且每个 item 中的 element 也多),由于每次滚动时需要绘制当前屏的每个 element,这需要占用大量的 ui 资源,所以就会有一些卡顿
* 为了解决这个问题 win8.1 给出了两种解决方案
* 1、设置 GridView 或 ListView 的 ShowsScrollingPlaceholders 属性为 true(默认值),每次显示 item 时先会显示占位符(application 级的背景色块),然后再绘制内容
* 2、通过 GridView 或 ListView 的 ContainerContentChanging 事件,分步绘制 item 中的 element
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes; namespace Windows81.Controls.GridViewAndListView
{
public sealed partial class IncrementalData : Page
{
public IncrementalData()
{
this.InitializeComponent();
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
gridView.ItemsSource = TestData.GetEmployees(); // 默认值是 true,即为了保证流畅,每次显示 item 时先会显示占位符(application 级的背景色块),然后再绘制内容
// 本例演示 ContainerContentChanging 事件的使用,所以不会用到这个
gridView.ShowsScrollingPlaceholders = false;
} private void gridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 交由我处理吧
args.Handled = true; // 第 1 阶段绘制
// args.Phase.ToString(); // StackPanel templateRoot = (StackPanel)args.ItemContainer.ContentTemplateRoot;
Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");
TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");
TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale"); // 显示自定义占位符(也可以不用这个,而是直接显示 item 的背景)
placeholderRectangle.Opacity = ; // 除了占位符外,所有 item 全部暂时不绘制
lblName.Opacity = ;
lblAge.Opacity = ;
lblIsMale.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowName);
} private void ShowName(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 2 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
TextBlock lblName = (TextBlock)templateRoot.FindName("lblName"); // 绘制第 2 阶段的内容
lblName.Text = employee.Name;
lblName.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowAge);
} private void ShowAge(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 3 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge"); // 绘制第 3 阶段的内容
lblAge.Text = employee.Age.ToString();
lblAge.Opacity = ; // 开始下一阶段的绘制
args.RegisterUpdateCallback(ShowIsMale);
} private void ShowIsMale(ListViewBase sender, ContainerContentChangingEventArgs args)
{
// 第 4 阶段绘制
// args.Phase.ToString(); // Employee employee = (Employee)args.Item;
SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale"); // 绘制第 4 阶段的内容
lblIsMale.Text = employee.IsMale.ToString();
lblIsMale.Opacity = ; // 隐藏自定义占位符
placeholderRectangle.Opacity = ;
}
}
}

2、GridViewItemPresenter 和 ListViewItemPresenter - 用于设置控件的各种状态(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加载完后再显示,win8.1 的此方式是需要的时候才加载)
GridViewAndListView/ItemPresenter.xaml

<Page
x:Class="Windows81.Controls.GridViewAndListView.ItemPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows81.Controls.GridViewAndListView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Page.Resources>
<Style x:Key="MyGridViewItemPresenterTemplate" TargetType="GridViewItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<!--
GridViewItemPresenter 和 ListViewItemPresenter - 用于设置控件的各种状态(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加载完后再显示,win8.1 的此方式是需要的时候才加载)
Margin - item 的 margin
SelectionCheckMarkVisualEnabled - 是否显示选中状态的标记
SelectedBorderThickness - 选中状态的边框粗细
SelectedBackground - 选中状态的边框颜色
CheckBrush - 选中状态的图标(本例就是那个小对勾)
...... - 还有好多好多,看文档吧
-->
<GridViewItemPresenter Margin="10" SelectionCheckMarkVisualEnabled="True" SelectedBorderThickness="3" SelectedBackground="Red" CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources> <Grid Background="Transparent">
<GridView x:Name="gridView" Margin="120 0 0 0"
ItemContainerStyle="{StaticResource MyGridViewItemPresenterTemplate}"
SelectionMode="Multiple">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Height="100" Width="100" Background="Blue">
<TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
<TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
<TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Page>

GridViewAndListView/ItemPresenter.xaml.cs

/*
* GridViewItemPresenter 和 ListViewItemPresenter - 用于设置控件的各种状态(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加载完后再显示,win8.1 的此方式是需要的时候才加载)
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; namespace Windows81.Controls.GridViewAndListView
{
public sealed partial class ItemPresenter : Page
{
public ItemPresenter()
{
this.InitializeComponent();
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
gridView.ItemsSource = TestData.GetEmployees();
}
}
}

3、演示自定义 GridViewItemPresenter 和 ListViewItemPresenter 的使用
GridViewAndListView/MyItemPresenter.cs

/*
* 自定义 GridViewItemPresenter 和 ListViewItemPresenter
*
* 本例演示如何自定义一个 ContentPresenter 类,以用于 GridView(参见:ItemPresenterCustom.xaml)
*/ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes; namespace Windows81.Controls.GridViewAndListView
{
class MyItemPresenter : ContentPresenter
{
Grid _contentGrid = null; // item 的 grid(数据模板中的 Grid,也就是说要使用本例的这个 ContentPresenter 则数据模板中必须要用 Grid 做容器)
Rectangle _pointerOverBorder = null; // 鼠标经过
Rectangle _focusVisual = null; // 选中 PointerDownThemeAnimation _pointerDownAnimation = null;
Storyboard _pointerDownStoryboard = null; GridView _parentGridView; public MyItemPresenter()
: base()
{
base.Margin = new Thickness();
} protected override void OnApplyTemplate()
{
base.OnApplyTemplate(); var obj = VisualTreeHelper.GetParent(this);
while (!(obj is GridView))
{
obj = VisualTreeHelper.GetParent(obj);
}
_parentGridView = (GridView)obj; _contentGrid = (Grid)VisualTreeHelper.GetChild(this, );
} protected override bool GoToElementStateCore(string stateName, bool useTransitions)
{
base.GoToElementStateCore(stateName, useTransitions); switch (stateName)
{
// 正常状态
case "Normal":
HidePointerOverVisuals();
HideFocusVisuals();
if (useTransitions)
{
StopPointerDownAnimation();
}
break; // 选中状态
case "Focused":
case "PointerFocused":
ShowFocusVisuals();
break; // 取消选中状态
case "Unfocused":
HideFocusVisuals();
break; // 鼠标经过状态
case "PointerOver":
ShowPointerOverVisuals();
if (useTransitions)
{
StopPointerDownAnimation();
}
break; // 鼠标点击状态
case "Pressed":
case "PointerOverPressed":
if (useTransitions)
{
StartPointerDownAnimation();
}
break; default: break;
} return true;
} private void StartPointerDownAnimation()
{
if (_pointerDownStoryboard == null)
CreatePointerDownStoryboard(); _pointerDownStoryboard.Begin();
} private void StopPointerDownAnimation()
{
if (_pointerDownStoryboard != null)
_pointerDownStoryboard.Stop();
} private void ShowFocusVisuals()
{
if (!FocusElementsAreCreated())
CreateFocusElements(); _focusVisual.Opacity = ;
} private void HideFocusVisuals()
{
if (FocusElementsAreCreated())
_focusVisual.Opacity = ;
} private void ShowPointerOverVisuals()
{
if (!PointerOverElementsAreCreated())
CreatePointerOverElements(); _pointerOverBorder.Opacity = ;
} private void HidePointerOverVisuals()
{
if (PointerOverElementsAreCreated())
_pointerOverBorder.Opacity = ;
} private void CreatePointerDownStoryboard()
{
_pointerDownAnimation = new PointerDownThemeAnimation();
Storyboard.SetTarget(_pointerDownAnimation, _contentGrid); _pointerDownStoryboard = new Storyboard();
_pointerDownStoryboard.Children.Add(_pointerDownAnimation);
} private void CreatePointerOverElements()
{
_pointerOverBorder = new Rectangle();
_pointerOverBorder.IsHitTestVisible = false;
_pointerOverBorder.Opacity = ;
_pointerOverBorder.Fill = (SolidColorBrush)_parentGridView.Resources["PointerOverBrush"]; // 此资源要预先在 GridView 中定义 _contentGrid.Children.Insert(_contentGrid.Children.Count, _pointerOverBorder);
} private void CreateFocusElements()
{
_focusVisual = new Rectangle();
_focusVisual.IsHitTestVisible = false;
_focusVisual.Height = ;
_focusVisual.StrokeThickness = ;
_focusVisual.VerticalAlignment = VerticalAlignment.Bottom;
_focusVisual.Fill = (SolidColorBrush)_parentGridView.Resources["FocusBrush"]; // 此资源要预先在 GridView 中定义
_focusVisual.Stroke = (SolidColorBrush)_parentGridView.Resources["FocusBrush"]; // 此资源要预先在 GridView 中定义 _contentGrid.Children.Insert(, _focusVisual);
} private bool FocusElementsAreCreated()
{
return _focusVisual != null;
} private bool PointerOverElementsAreCreated()
{
return _pointerOverBorder != null;
}
}
}

GridViewAndListView/ItemPresenterCustom.xaml

<Page
x:Class="Windows81.Controls.GridViewAndListView.ItemPresenterCustom"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows81.Controls.GridViewAndListView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <Page.Resources>
<Style x:Key="MyGridViewItemPresenterTemplate" TargetType="GridViewItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<!--
自定义 GridViewItemPresenter 和 ListViewItemPresenter
关于 GridViewItemPresenter 和 ListViewItemPresenter 的说明参见:ItemPresenter.xaml MyItemPresenter 参见 MyItemPresenter.cs
-->
<local:MyItemPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources> <Grid Background="Transparent">
<GridView x:Name="gridView" Margin="120 0 0 0"
ItemContainerStyle="{StaticResource MyGridViewItemPresenterTemplate}"
SelectionMode="Multiple">
<GridView.Resources>
<!--MyItemPresenter.cs 中需要用到的资源-->
<SolidColorBrush x:Name="PointerOverBrush" Color="#50505050"/>
<SolidColorBrush x:Name="FocusBrush" Color="#ffff0000"/>
</GridView.Resources>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="100" Width="100" Background="Blue">
<TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Page>

GridViewAndListView/ItemPresenterCustom.xaml.cs

/*
* 演示自定义 GridViewItemPresenter 和 ListViewItemPresenter 的使用
*
* 关于 GridViewItemPresenter 和 ListViewItemPresenter 的说明参见:ItemPresenter.xaml
*/ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; namespace Windows81.Controls.GridViewAndListView
{
public sealed partial class ItemPresenterCustom : Page
{
public ItemPresenterCustom()
{
this.InitializeComponent();
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
gridView.ItemsSource = TestData.GetEmployees();
}
}
}

OK
[源码下载]