首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 Store Apps 開發實戰(12):GridView 進階特性 — 項目拖曳排序、可變項目尺寸與分組顯示

     編輯:關於.NET

前言

GridView 作為 WinRT 最強大的集合控件之一,內建支援 項目拖曳重新排序、拖出刪除、可變項目尺寸、資料分組顯示 等進階功能。本文將透過三個完整範例,逐一解析這些特性的實作細節與關鍵 API。

一、項目拖曳排序與拖出刪除

核心屬性:

  • CanDragItems="True":允許項目被拖曳
  • CanReorderItems="True":允許在 GridView 內透過拖曳排序
  • AllowDrop="True":允許項目被放置(GridView 內或外部容器)
  • DragItemsStarting 事件:拖曳開始時取得被拖曳項目

XAML 佈局

<Page.Resources>
    <DataTemplate x:Key="ItemTemplate">
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Name}" FontSize="14.667" HorizontalAlignment="Left"/>
            <TextBlock Text="{Binding Age}" FontSize="14.667" HorizontalAlignment="Left"/>
        </StackPanel>
    </DataTemplate>

    <Style x:Key="ItemContainerStyle" TargetType="GridViewItem">
        <Setter Property="Width" Value="292"/>
        <Setter Property="Height" Value="80"/>
        <Setter Property="Margin" Value="0"/>  <!-- 無法完全移除間距,需設 -4 -->
        <Setter Property="Background" Value="Blue"/>
    </Style>

    <ItemsPanelTemplate x:Key="ItemsPanel">
        <WrapGrid MaximumRowsOrColumns="3" Orientation="Vertical"
                VerticalChildrenAlignment="Top" HorizontalChildrenAlignment="Left"/>
    </ItemsPanelTemplate>
</Page.Resources>

<Grid Background="Transparent">
    <StackPanel Margin="120 0 0 0">
        <TextBlock x:Name="lblMsg" FontSize="14.667"
                   Text="透過拖曳 GridView 中的 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"/>

        <!-- 拖曳到此區域刪除項目 -->
        <Grid x:Name="gridDelete" Margin="0 10 0 0"
              AllowDrop="True"
              Drop="gridDelete_Drop"
              DragEnter="gridDelete_DragEnter"
              DragLeave="gridDelete_DragLeave"
              DragOver="gridDelete_DragOver">
            <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>

後台邏輯:排序與刪除

public sealed partial class DragItem : Page
{
    private ObservableCollection<Employee> _dataSource;
    private Employee _draggingEmployee;

    public DragItem()
    {
        this.InitializeComponent();
        _dataSource = new ObservableCollection<Employee>(TestData.GetEmployees());
        gridView.ItemsSource = _dataSource;

        // 監聽 ItemsChanged 以在 Drop 後更新資料源順序
        gridView.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
    }

    void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
    {
        if (e.OldPosition.Index > -1)
        {
            var oldIndex = _dataSource.IndexOf(_draggingEmployee);
            var newIndex = e.Position.Index + e.Position.Offset;
            _dataSource.Move(oldIndex, newIndex); // ObservableCollection 擴充方法
            _draggingEmployee = null;
        }
    }

    private void gridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        _draggingEmployee = e.Items.First() as Employee;
        // e.Data 可設定 DataPackage 跨應用拖曳,本例用欄位簡化
    }

    private void gridDelete_Drop(object sender, DragEventArgs e)
    {
        _dataSource.Remove(_draggingEmployee);
        _draggingEmployee = null;
        Debug.WriteLine("Drop");
    }

    private void gridDelete_DragEnter(object sender, DragEventArgs e) => Debug.WriteLine("DragEnter");
    private void gridDelete_DragLeave(object sender, DragEventArgs e) => Debug.WriteLine("DragLeave");
    private void gridDelete_DragOver(object sender, DragEventArgs e) => Debug.WriteLine("DragOver");
}

關鍵點總結

  1. DragItemsStarting 取得被拖曳物件,暫存於欄位
  2. ItemContainerGenerator.ItemsChanged 事件在 Drop 完成後觸發,透過 e.Position.Index + e.Position.Offset 計算新索引
  3. 外部刪除區域只需設 AllowDrop="True" 並處理 Drop 事件移除資料源項目
  4. DataPackage 可用於跨應用拖曳/剪貼簿/Share Contract,本例為簡化使用私有欄位傳遞

二、可變項目尺寸 — VariableSizedWrapGrid + 自定義 GridView

預設 GridView 項目等寬等高。若需每個項目佔據不同列/行合併數,必須:

  1. 使用 VariableSizedWrapGrid 作為 ItemsPanel
  2. 繼承 GridView 並覆寫 PrepareContainerForItemOverride,依資料物件屬性設定 VariableSizedWrapGrid.ColumnSpanProperty / RowSpanProperty

資料模型

class ColorModel
{
    public string ColorName { get; set; }
    public SolidColorBrush ColorValue { get; set; }
    public int ColSpan { get; set; }  // 列合併數
    public int RowSpan { get; set; }  // 行合併數
}

自定義 MyGridView

public class MyGridView : Windows.UI.Xaml.Controls.GridView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        try
        {
            dynamic d = item;
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, d.ColSpan);
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, d.RowSpan);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
        }
        finally
        {
            base.PrepareContainerForItemOverride(element, item);
        }
    }
}

XAML 使用

<Page.Resources>
    <DataTemplate x:Key="ItemTemplate">
        <Grid Background="{Binding ColorValue}">
            <Grid Background="Black" VerticalAlignment="Top" 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"/>
        <Setter Property="Margin" Value="-4"/>  <!-- 移除項目間距 -->
    </Style>

    <ItemsPanelTemplate x:Key="ItemsPanel">
        <VariableSizedWrapGrid MaximumRowsOrColumns="4"
                               Orientation="Vertical"
                               ItemWidth="100" ItemHeight="100"
                               Height="400"/>
    </ItemsPanelTemplate>
</Page.Resources>

<local:MyGridView x:Name="gridView" Height="400"
                  Background="Yellow"
                  ItemTemplate="{StaticResource ItemTemplate}"
                  ItemContainerStyle="{StaticResource ItemContainerStyle}"
                  ItemsPanel="{StaticResource ItemsPanel}"
                  IsItemClickEnabled="False" IsSwipeEnabled="False"
                  SelectionMode="None"
                  ScrollViewer.VerticalScrollBarVisibility="Auto"
                  ScrollViewer.HorizontalScrollBarVisibility="Auto"/>

後台綁定隨機尺寸資料

private void BindData()
{
    var random = new Random();
    var colors = typeof(Colors)
        .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;
}

注意事項

  • VariableSizedWrapGrid 不繼承 VirtualizingPanel,大量資料時無虛擬化,效能較 WrapGrid 差
  • ItemWidth/ItemHeight 定義基礎網格尺寸,ColumnSpan/RowSpan 為倍數
  • 項目容器需設 HorizontalContentAlignment=Stretch、VerticalContentAlignment=Stretch 才能填滿合併區域

三、分組顯示 — 參考 Index.xaml 與 SemanticZoom

分組功能源自 ItemsControl 基類,透過 CollectionViewSource.IsSourceGrouped=true、ItemsPath 指定子集合屬性,GridView.GroupStyle 定義分組標題、容器、面板。完整範例請參考本系列 第 13 篇 SemanticZoom 的 Index.xaml 與 Index.xaml.cs。

四、小結

GridView 進階三大特性:

  1. 拖曳排序/刪除:CanDragItems、CanReorderItems、AllowDrop、DragItemsStarting、ItemContainerGenerator.ItemsChanged
  2. 可變尺寸:VariableSizedWrapGrid + 覆寫 PrepareContainerForItemOverride 設定 ColumnSpan/RowSpan
  3. 分組顯示:CollectionViewSource + GroupStyle(HeaderTemplate、ContainerStyle、Panel)

善用這些特性,可打造極具互動性與視覺豐富度的集合介面。


原始碼下載:https://files.cnblogs.com/webabcd/Windows8.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved