重新想象 Windows 8 Store Apps (12) - 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示
作者:webabcd
介绍
重新想象 Windows 8 Store Apps 之 GridView
- 拖动项 - 在 GridView 内拖动 item 以对 item 排序, 拖动 item 到 GridView 外的指定位置以删除 item
- 项尺寸可变 - 指定 GirdView 中每个 item 所占尺寸
- 分组显示 - 分组显示集合数据
示例
1、演示如何在 GridView 内拖动 item 以对 item 排序,以及如何拖动 item 到 GridView 外的指定位置以删除 item
GridView/DragItem.xaml
<Page x:Class="XamlDemo.Controls.GridView.DragItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.GridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="ItemTemplate"> <StackPanel Orientation="Vertical"> <TextBlock TextWrapping="Wrap" FontSize="14.667" Text="{Binding Name}" HorizontalAlignment="Left" /> <TextBlock TextWrapping="Wrap" FontSize="14.667" Text="{Binding Age}" HorizontalAlignment="Left"/> </StackPanel> </DataTemplate> <Style x:Key="ItemContainerStyle" TargetType="GridViewItem"> <Setter Property="Width" Value="292" /> <Setter Property="Height" Value="80" /> <!-- 即使将 Margin 设置为“0”,也无法去掉 item 之间的 margin 如果想要去掉 item 之间的 margin,请将此 Margin 属性设置为“-4” --> <Setter Property="Margin" Value="0" /> <Setter Property="Background" Value="Blue" /> </Style> <ItemsPanelTemplate x:Key="ItemsPanel"> <!-- 注:WrapGrid 继承了 VirtualizingPanel,而 VariableSizedWrapGrid 并未继承 VirtualizingPanel --> <WrapGrid MaximumRowsOrColumns="3" Orientation="Vertical" VerticalChildrenAlignment="Top" HorizontalChildrenAlignment="Left" /> </ItemsPanelTemplate> </Page.Resources> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" Text="通过拖动 GirdView 中的 Item 进行排序" /> <GridView x:Name="gridView" VerticalAlignment="Top" Margin="0 10 10 0" BorderThickness="1" BorderBrush="Red" Background="LightBlue" ItemTemplate="{StaticResource ItemTemplate}" ItemContainerStyle="{StaticResource ItemContainerStyle}" ItemsPanel="{StaticResource ItemsPanel}" IsSwipeEnabled="True" IsItemClickEnabled="True" CanDragItems="True" CanReorderItems="True" AllowDrop="True" DragItemsStarting="gridView_DragItemsStarting_1" /> <!--拖动 item 到此处以删除 item--> <Grid Name="gridDelete" Margin="0 10 0 0" AllowDrop="True" Drop="gridDelete_Drop_1" DragEnter="gridDelete_DragEnter_1" DragLeave="gridDelete_DragLeave_1" DragOver="gridDelete_DragOver_1"> <Rectangle Width="300" Height="100" StrokeThickness="1" StrokeDashArray="2" Stroke="Red" Fill="Blue" /> <TextBlock FontSize="26.667" Text="拖动到此处以删除" TextAlignment="Center" VerticalAlignment="Center" /> </Grid> </StackPanel> </Grid> </Page>
GridView/DragItem.xaml.cs
/* * 演示如何在 GridView 内拖动 item 以对 item 排序,以及如何拖动 item 到 GridView 外的指定位置以删除 item * * GridView - 网格控件 * CanDragItems - item 是否可被拖动 * CanReorderItems - 是否可通过拖动 item 来排序 * AllowDrop - 是否可在 GridView 中 drop * DragItemsStarting - item 开始被拖动时所触发的事件(事件参数 DragItemsStartingEventArgs) * * DragItemsStartingEventArgs * Items - 被拖动的 items 集合 * Cancel - 是否取消拖动操作 * Data - 一个 DataPackage 类型的对象,用于传递数据(与 DataPackage 在剪切板和 Share Contract 中的作用一样) * * * 注: * drag-drop 间传递数据,clipboard 间传递数据,Share Contract 间传递数据,以及其他场景的数据传递均可通过 DataPackage 类型的对象来完成 * 本例没有通过 DataPackage 来传递数据(太麻烦),而是通过一个私有字段来传递数据(比较简单) */ using System.Collections.ObjectModel; using Windows.UI.Xaml.Controls; using System.Linq; using XamlDemo.Model; using Windows.UI.Xaml; using System.Diagnostics; namespace XamlDemo.Controls.GridView { public sealed partial class DragItem : Page { // 数据源 private ObservableCollection<Employee> _dataSource; // 拖动中的 Employee 对象 private Employee _draggingEmployee; public DragItem() { this.InitializeComponent(); // 绑定数据 _dataSource = new ObservableCollection<Employee>(TestData.GetEmployees()); gridView.ItemsSource = _dataSource; // GridView 中的 items 发生变化时触发的事件 gridView.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged; } void ItemContainerGenerator_ItemsChanged(object sender, Windows.UI.Xaml.Controls.Primitives.ItemsChangedEventArgs e) { if (e.OldPosition.Index > -1) { // 在 GridView 中 drop 了 item,且排序发生了变化 var oldIndex = _dataSource.IndexOf(_draggingEmployee); // 被拖动的 Employee 对象的原位置 var newIndex = e.Position.Index + e.Position.Offset; // 被拖动的 Employee 对象的新位置 // 修改数据源 _dataSource.Move(oldIndex, newIndex); _draggingEmployee = null; } } // GridView 中的 item 开始被拖动时 private void gridView_DragItemsStarting_1(object sender, DragItemsStartingEventArgs e) { _draggingEmployee = e.Items.First() as Employee; } // GridView 中的 item 被 drop 到了指定的位置后 private void gridDelete_Drop_1(object sender, DragEventArgs e) { // 从数据源中删除指定的 Employee 对象 _dataSource.Remove(_draggingEmployee); _draggingEmployee = null; // 在 gridDelete 放下了拖动项 Debug.WriteLine("Drop"); } private void gridDelete_DragEnter_1(object sender, DragEventArgs e) { // 拖动项被拖进 gridDelete 了 Debug.WriteLine("DragEnter"); } private void gridDelete_DragLeave_1(object sender, DragEventArgs e) { // 拖动项被拖出 gridDelete 了 Debug.WriteLine("DragLeave"); } private void gridDelete_DragOver_1(object sender, DragEventArgs e) { // 拖动项在 gridDelete 上面拖动着 Debug.WriteLine("DragOver"); } } }
2、演示如何指定 GirdView 中每个 item 所占尺寸
GridView/ColorModel.cs
using Windows.UI.Xaml.Media; namespace XamlDemo.Controls.GridView { /// <summary> /// 用于绑定到 VariableSized.xaml 中的 GridView 的数据实体模型 /// </summary> class ColorModel { public string ColorName { get; set; } public SolidColorBrush ColorValue { get; set; } // 此对象所占的网格的列合并数 public int ColSpan { get; set; } // 此对象所占的网格的行合并数 public int RowSpan { get; set; } } }
GridView/MyGridView.cs
/* * 此控件可以指定 GridView 的每个 item 所占网格的列合并数和行合并数 */ using System; using System.Diagnostics; namespace XamlDemo.Controls.GridView { public class MyGridView : Windows.UI.Xaml.Controls.GridView { protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item) { try { // 指定 VariableSizedWrapGrid 的 ColumnSpan 和 RowSpan dynamic dynamicItem = item; element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, dynamicItem.ColSpan); element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, dynamicItem.RowSpan); } catch(Exception ex) { // 当有异常情况发生时(比如:item 没有 ColSpan 属性或 RowSpan 属性) Debug.WriteLine(ex.ToString()); element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, 1); element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, 1); } finally { base.PrepareContainerForItemOverride(element, item); } } } }
GridView/VariableSized.xaml
<Page x:Class="XamlDemo.Controls.GridView.VariableSized" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.GridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="ItemTemplate"> <Grid Background="{Binding ColorValue}"> <Grid Background="Black" VerticalAlignment="Top" HorizontalAlignment="Stretch" Opacity="0.7"> <TextBlock Text="{Binding ColorName}" /> </Grid> </Grid> </DataTemplate> <Style x:Key="ItemContainerStyle" TargetType="GridViewItem"> <Setter Property="VerticalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <!-- 即使将 Margin 设置为“0”,也无法去掉 item 之间的 margin 如果想要去掉 item 之间的 margin,请将此 Margin 属性设置为“-4” --> <Setter Property="Margin" Value="-4" /> </Style> <ItemsPanelTemplate x:Key="ItemsPanel"> <!-- 注:WrapGrid 继承了 VirtualizingPanel,而 VariableSizedWrapGrid 并未继承 VirtualizingPanel ItemWidth, ItemHeight - 每个网格的宽和高 ColumnSpan, RowSpan - item 所在网格的列合并数和行合并数,本例在后台指定了这两个属性,参见 MyGridView.cs --> <VariableSizedWrapGrid MaximumRowsOrColumns="4" Orientation="Vertical" ItemWidth="100" ItemHeight="100" Height="400" /> </ItemsPanelTemplate> </Page.Resources> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <!-- 使用 MyGridView 控件,其重写了 GridView 的 PrepareContainerForItemOverride() 方法,详见 MyGridView.cs --> <local:MyGridView x:Name="gridView" Height="400" VerticalAlignment="Top" Margin="0 10 10 0" Background="Yellow" ItemTemplate="{StaticResource ItemTemplate}" ItemContainerStyle="{StaticResource ItemContainerStyle}" ItemsPanel="{StaticResource ItemsPanel}" IsItemClickEnabled="False" IsSwipeEnabled="False" SelectionMode="None" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"> </local:MyGridView> </StackPanel> </Grid> </Page>
GridView/VariableSized.xaml.cs
/* * 演示如何指定 GirdView 中每个 item 所占尺寸 * GridView 是一个网格控件,这里所谓的每个 item 所占尺寸,其本质上就是 item 所在网格的列合并数和行合并数 * * 要实现此需求的话: * 1、必须使用 VariableSizedWrapGrid,具体见 VariableSized.xaml * 2、需要重写 GridView 的 PrepareContainerForItemOverride() 方法,具体见 MyGridView.cs */ using System.Reflection; using System.Linq; using System.Collections.Generic; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; using System; using Windows.UI; namespace XamlDemo.Controls.GridView { public sealed partial class VariableSized : Page { public VariableSized() { this.InitializeComponent(); BindData(); } private void BindData() { Random random = new Random(); // 获取 Windows.UI.Colors 的全部数据 List<ColorModel> colors = typeof(Colors) // typeof 在 System.Reflection 命名空间下 .GetRuntimeProperties() .Select(c => new ColorModel { ColorName = c.Name, ColorValue = new SolidColorBrush((Color)c.GetValue(null)), ColSpan = random.Next(1, 3), // 此对象所占网格的列合并数 RowSpan = random.Next(1, 3) // 此对象所占网格的行合并数 }) .ToList(); // 绑定数据 gridView.ItemsSource = colors; } } }
3、演示如何分组显示集合数据(关于分组的示例会和之后的 SemanticZoom 一起写)
GridView/Group.xaml
<Page x:Class="XamlDemo.Controls.GridView.Group" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Controls.GridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667"> <Run>关于 GridView 的分组显示请参见本 app 的索引页 Index.xaml 和 Index.xaml.cs</Run> <LineBreak /> <Run>分组的功能来源于 ItemsControl(GridView, ListView, FlipView, ListBox 等均继承了 ItemsControl)</Run> </TextBlock> </StackPanel> </Grid> </Page>
OK
[源码下载]