1. 程式人生 > >背水一戰 Windows 10 (56) - 控件(集合類): ListViewBase - 基礎知識, 拖動項

背水一戰 Windows 10 (56) - 控件(集合類): ListViewBase - 基礎知識, 拖動項

tar accepted 類型 idv .get footer ati model 變化

原文:背水一戰 Windows 10 (56) - 控件(集合類): ListViewBase - 基礎知識, 拖動項

[源碼下載]


背水一戰 Windows 10 (56) - 控件(集合類): ListViewBase - 基礎知識, 拖動項



作者:webabcd


介紹
背水一戰 Windows 10 之 控件(集合類 - ListViewBase)

  • 基礎知識
  • 拖動項



示例
1、ListViewBase 的基礎知識
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo1.xaml

<Page
    x:Class
="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:common="using:Windows10.Common"> <Grid Background="Transparent"> <Grid Margin="10 0 10 10"> <StackPanel Orientation="Vertical"> <
TextBlock Name="lblMsg1" /> <TextBlock Name="lblMsg2" Margin="0 10 0 0" /> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="0 10 0 0"> <CheckBox Name="chkIsItemClickEnabled" Content="IsItemClickEnabled" Margin="10 0 0 0" /> <CheckBox Name="chkShowsScrollingPlaceholders" Content="ShowsScrollingPlaceholders" Margin="10 0 0 0" /> <CheckBox Name="chkIsMultiSelectCheckBoxEnabled" Content="IsMultiSelectCheckBoxEnabled" Margin="10 0 0 0" /> <ComboBox Name="cmbSelectionMode" PlaceholderText="SelectionMode" SelectionChanged="cmbSelectionMode_SelectionChanged" Margin="10 0 0 0"> <ComboBoxItem>None</ComboBoxItem> <ComboBoxItem>Single</ComboBoxItem> <ComboBoxItem>Multiple</ComboBoxItem> <ComboBoxItem>Extended</ComboBoxItem> </ComboBox> </StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="0 10 0 0"> <Button Name="buttonScrollDefault" Content="滾動到第 101 條數據 ScrollIntoViewAlignment.Default" Click="buttonScrollDefault_Click" /> <Button Name="buttonScrollLeading" Content="滾動到第 101 條數據 ScrollIntoViewAlignment.Leading" Click="buttonScrollLeading_Click" Margin="10 0 0 0" /> <Button Name="buttonSelect" Content="選中第 3 到第 6 項" Click="buttonSelect_Click" Margin="10 0 0 0" /> </StackPanel> </StackPanel> <!-- ListViewBase(基類) - 列表控件基類 Header, HeaderTemplate, Footer, HeaderTransitions - 顧名思義,不用解釋 HeaderTransitions - header 的過渡效果 FooterTransitions - footer 的過渡效果 IsItemClickEnabled - 點擊 item 時是否會觸發 ItemClick 事件(默認值為 false) IsSwipeEnabled - 是否支持 swipe 操作(對於 ListView 來說,在觸摸模式下,左右輕掃 item 稱之為 swipe; 對於 GridView 來說,在觸摸模式下,上下輕掃 item 稱之為 swipe) 註:經測試,在 uwp 中這個屬性無效(不支持 item 的 swipe 了) SelectionMode - item 的選中模式 None - 不能被選中 Single - 只能單選(默認值) Multiple - 支持多選,且支持輔助鍵多選(ctrl 或 shift) Extended - 支持多選,且支持輔助鍵多選(ctrl 或 shift) IsMultiSelectCheckBoxEnabled - 在 SelectionMode 為 Multiple 時,是否為每個 item 顯示復選框 ShowsScrollingPlaceholders - 在大數據量滾動時,為了保證流暢,是否每次顯示 item 時先顯示占位符(尚不清楚怎麽修改這個占位符的背景色),然後再繪制內容 可以用 GridView 來呈現大量數據,以便查看滾動時 ShowsScrollingPlaceholders 帶來的效果 ItemClick - 單擊 item 時觸發的事件(IsItemClickEnabled 為 false 時不會觸發這個事件) SelectionChanged - 選中項發生變化時觸發的事件(這個來自 Selector 類,SelectionMode 為 None 時不會觸發這個事件) ContainerContentChanging - 數據虛擬化時,項容器的內容發生變化時觸發的事件(僅 ItemsStackPanel 和 ItemsWrapGrid 有效) ChoosingItemContainer - 數據虛擬化時,為項選擇容器時觸發的事件(僅 ItemsStackPanel 和 ItemsWrapGrid 有效) ChoosingGroupHeaderContainer - 為每組的 header 選擇容器時觸發的事件(關於數據分組請參見 /Controls/CollectionControl/ItemsControlDemo/ItemsControlDemo4.xaml) 註: 1、ListViewBase 的滾動來自其內的 ScrollViewer 控件,可以通過對應的附加屬性和靜態方法對其設置。關於 ScrollViewer 請參見:/Controls/ScrollViewerDemo/ 2、ListView 的 ItemContainer 是 ListViewItem,如果需要設置 item 的選中樣式之類的就設置 ItemContainerStyle 即可 關於 ListView 的默認 ItemContainerStyle 請參見 C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic\generic.xaml 中的 ListViewItemExpanded,需要修改的話請在此基礎上修改 --> <ListView x:Name="listView" VerticalAlignment="Top" HorizontalAlignment="Left" ItemsSource="{x:Bind Data}" Margin="0 150 10 10" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" SelectionMode="Single" IsItemClickEnabled="{Binding IsChecked, ElementName=chkIsItemClickEnabled}" ShowsScrollingPlaceholders ="{Binding IsChecked, ElementName=chkShowsScrollingPlaceholders}" IsMultiSelectCheckBoxEnabled="{Binding IsChecked, ElementName=chkIsMultiSelectCheckBoxEnabled}" SelectionChanged="listView_SelectionChanged" ItemClick="listView_ItemClick"> <ListView.ItemTemplate> <DataTemplate x:DataType="common:Employee"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{x:Bind Name, Mode=TwoWay}" /> <TextBlock Text="{x:Bind Age, Mode=TwoWay}" Margin="10 0 0 0" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> <ListView.HeaderTemplate> <DataTemplate> <TextBlock Text="header" /> </DataTemplate> </ListView.HeaderTemplate> <ListView.HeaderTransitions> <TransitionCollection> <EntranceThemeTransition FromHorizontalOffset="100" /> </TransitionCollection> </ListView.HeaderTransitions> <ListView.FooterTemplate> <DataTemplate> <TextBlock Text="footer" /> </DataTemplate> </ListView.FooterTemplate> <ListView.FooterTransitions> <TransitionCollection> <EntranceThemeTransition /> </TransitionCollection> </ListView.FooterTransitions> </ListView> </Grid> </Grid> </Page>

Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo1.xaml.cs

/*
 * ListViewBase(基類) - 列表控件基類(繼承自 Selector, 請參見 /Controls/SelectionControl/SelectorDemo.xaml)
 *     SelectedItems - 被選中的 items 集合(只讀)
 *     SelectedRanges - 被選中的 items 的範圍(只讀)
 *     SelectAll() - 選中全部 items
 *     SelectRange(ItemIndexRange itemIndexRange) - 選中指定範圍的 items
 *     DeselectRange(ItemIndexRange itemIndexRange) - 取消選中指定範圍的 items
 *     ScrollIntoView(object item, ScrollIntoViewAlignment alignment) - 滾動到指定的 item
 *         ScrollIntoViewAlignment.Default - 不好解釋,請自己看演示效果
 *         ScrollIntoViewAlignment.Leading - 不好解釋,請自己看演示效果
 * 
 * ItemIndexRange - items 的範圍
 *     FirstIndex - 範圍的第一個 item 的索引位置
 *     LastIndex - 範圍的最後一個 item 的索引位置
 *     Length - 範圍的長度
 *     
 * 
 * 註:
 * ListViewBase 實現了 ISemanticZoomInformation 接口,所以可以在 SemanticZoom 的兩個視圖間有關聯地切換。關於 ISemanticZoomInformation 請參見 /Controls/CollectionControl/SemanticZoomDemo/ISemanticZoomInformationDemo.xaml
 * 
 * 
 * 本例用於演示 ListViewBase 的基礎知識
 */

using System;
using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using System.Linq;
using Windows10.Common;
using Windows.UI.Xaml;
using Windows.UI.Popups;

namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
{
    public sealed partial class ListViewBaseDemo1 : Page
    {
        public ObservableCollection<Employee> Data { get; set; } = TestData.GetEmployees(10000);

        public ListViewBaseDemo1()
        {
            this.InitializeComponent();
        }

        // 單擊行為的事件
        private void listView_ItemClick(object sender, ItemClickEventArgs e)
        {
            // 獲取被單擊的 item 的數據
            lblMsg1.Text = "被單擊的 employee 的 name 為:" + (e.ClickedItem as Employee).Name;
        }

        // 選中行為的事件
        private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // e.RemovedItems - 本次事件中,被取消選中的項
            // e.AddedItems - 本次事件中,新被選中的項

            lblMsg2.Text = $"新被選中的 item 共 {e.AddedItems.Count.ToString()} 條, 新被取消選中的 item 共 {e.RemovedItems.Count.ToString()} 條";
        }

        private void cmbSelectionMode_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            listView.SelectionMode = (ListViewSelectionMode)Enum.Parse(typeof(ListViewSelectionMode), (e.AddedItems[0] as ComboBoxItem).Content.ToString());
        }

        private void buttonScrollDefault_Click(object sender, RoutedEventArgs e)
        {
             listView.ScrollIntoView(Data.Skip(100).First(), ScrollIntoViewAlignment.Default);
        }

        private void buttonScrollLeading_Click(object sender, RoutedEventArgs e)
        {
            listView.ScrollIntoView(Data.Skip(100).First(), ScrollIntoViewAlignment.Leading);
        }

        private async void buttonSelect_Click(object sender, RoutedEventArgs e)
        {
            if (listView.SelectionMode == ListViewSelectionMode.Multiple || listView.SelectionMode == ListViewSelectionMode.Extended)
            {
                // 選中第 3, 4, 5, 6 項
                ItemIndexRange iir = new ItemIndexRange(2, 4);
                listView.SelectRange(iir);
            }
            else
            {
                MessageDialog messageDialog = new MessageDialog("SelectionMode 必須是 Multiple 或 Extended", "提示");
                await messageDialog.ShowAsync();
            }
        }
    }
}


2、ListViewBase 內拖動 item
Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo2.xaml

<Page
    x:Class="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    
    xmlns:common="using:Windows10.Common">

    <Page.Resources>
        <DataTemplate x:Key="ItemTemplate" x:DataType="common:Employee">
            <StackPanel Background="Orange" Width="100">
                <TextBlock TextWrapping="Wrap" Text="{x:Bind Name}" HorizontalAlignment="Left" />
                <TextBlock TextWrapping="Wrap" Text="{x:Bind Age}" HorizontalAlignment="Left"/>
            </StackPanel>
        </DataTemplate>
    </Page.Resources>

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <Button Name="buttonMove" Content="通過 api 將 gridView1 中的第 1 項數據移動到第 3 項數據的位置" Click="buttonMove_Click" Margin="0 0 0 10" />
            
            <!--
                ListViewBase(基類) - 列表控件基類
                    CanDragItems - item 是否可被拖動
                    CanReorderItems - 是否可通過拖動 item 來排序
                        如果設置為 true 的話,則除了在 UI 上可以拖動排序外,排序後的結果也會同步到數據源(可以通過 ObservableCollection<T> 的 CollectionChanged 事件捕獲到)
                    AllowDrop - 是否可在 ListViewBase 中 drop(來自 UIElement)
                    ReorderMode - 經測試,無效
                    DragItemsStarting - 開始 item 拖動時觸發的事件
                    DragItemsCompleted - 完成 item 拖動時觸發的事件
            -->

            <!--
                在內部拖動可以排序,可以拖動到 gridView2 以復制,可以拖動到 borderDelete 以刪除
            -->
            <GridView Name="gridView1" Margin="5" VerticalAlignment="Top" Height="100" ItemsSource="{x:Bind Data1}" Background="White"
                      ItemTemplate="{StaticResource ItemTemplate}"
                      CanDragItems="True" CanReorderItems="True" AllowDrop="True"
                      DragItemsStarting="gridView_DragItemsStarting" 
                      DragItemsCompleted="gridView_DragItemsCompleted" />

            <!--
                在內部拖動可以排序,可以拖動到 borderDelete 以刪除
            -->
            <GridView Name="gridView2" Margin="5" VerticalAlignment="Top" Height="100" ItemsSource="{x:Bind Data2}" Background="White"
                      ItemTemplate="{StaticResource ItemTemplate}"
                      CanDragItems="True" CanReorderItems="True" AllowDrop="True"
                      DragItemsStarting="gridView_DragItemsStarting" 
                      DragItemsCompleted="gridView_DragItemsCompleted" 
                      DragEnter="gridView2_DragEnter"
                      Drop="gridView2_Drop"/>

            <!--
                可以拖動到 gridView2 以復制
            -->
            <TextBlock Name="lblEmployee" Margin="5" Foreground="Orange" Text="{x:Bind Path=Employee.Name}" 
                       PointerPressed="lblEmployee_PointerPressed" DragStarting="lblEmployee_DragStarting" />

            <!--
                拖動 gridView1 或 gridView2 中的 item 到此處以刪除
            -->
            <Border Name="borderDelete" Margin="5" Width="300" Height="100" BorderThickness="1" BorderBrush="Red" Background="Blue"
                    AllowDrop="True" Drop="borderDelete_Drop" DragEnter="borderDelete_DragEnter" DragLeave="borderDelete_DragLeave" DragOver="borderDelete_DragOver">
                <TextBlock FontSize="32" Text="拖動到此處以刪除" TextAlignment="Center" VerticalAlignment="Center" />
            </Border>

            <TextBlock Name="lblMsg" Margin="5" Text="通過拖動 GirdView 中的 Item 進行排序" />

        </StackPanel>
    </Grid>
</Page>

Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo2.xaml.cs

/*
 * ListViewBase(基類) - 列表控件基類(繼承自 Selector, 請參見 /Controls/SelectionControl/SelectorDemo.xaml)
 * 
 *      
 * DragItemsStartingEventArgs
 *     Items - 被拖動的 items 集合
 *     Cancel - 是否取消拖動操作
 *     Data - 一個 DataPackage 類型的對象,用於傳遞數據
 *     
 * DragItemsCompletedEventArgs
 *     DropResult - drop 的結果(None, Copy, Move, Link)
 *     Items - 被拖動的 items 集合
 *     
 * 
 * 註:
 * 1、drag-drop 傳遞數據,剪切板傳遞數據,分享傳遞數據,以及其他場景的數據傳遞均通過 DataPackage 類型的對象來完成
 * 2、本例通過一個私有字段傳遞數據,通過 DataPackage 傳遞數據請參見:/Controls/BaseControl/UIElementDemo/DragDropDemo.xaml
 * 3、關於 UIElement 拖放的詳細說明請參見:/Controls/BaseControl/UIElementDemo/DragDropDemo.xaml
 * 
 * 
 * 本例用於演示如何在 ListViewBase 內拖動 item 以對 item 排序,以及如何拖動 item 到 ListViewBase 外的指定位置以刪除 item,以及如何拖動一個 UIElement 到 ListViewBase 內以添加這個 item
 */

using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
using System.Linq;
using Windows.UI.Xaml;
using System.Diagnostics;
using Windows10.Common;
using Windows.ApplicationModel.DataTransfer;
using System.Collections.Specialized;
using System;
using Windows.UI.Xaml.Input;

namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
{
    public sealed partial class ListViewBaseDemo2 : Page
    {
        // gridView1 的數據源
        public ObservableCollection<Employee> Data1 { get; set; } = new ObservableCollection<Employee>(TestData.GetEmployees());
        // gridView2 的數據源
        public ObservableCollection<Employee> Data2 { get; set; } = new ObservableCollection<Employee>();

        // lblEmployee 的數據源
        public Employee Employee { get; set; } = new Employee() { Name = "wanglei", Age = 36, IsMale = true };

        // 拖動中的 Employee 對象
        private Employee _draggingEmployee;

        public ListViewBaseDemo2()
        {
            this.InitializeComponent();

            // 這個用來證明在 gridView1 中拖動 item 排序時,其結果會同步到數據源
            Data1.CollectionChanged += Data1_CollectionChanged;
        }

        private void Data1_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            lblMsg.Text += Environment.NewLine;
            lblMsg.Text += $"Action: {e.Action}, OldStartingIndex: {e.OldStartingIndex}, NewStartingIndex: {e.NewStartingIndex}";
        }

        // 開始 item 拖動
        private void gridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
        {
            _draggingEmployee = e.Items.First() as Employee;
        }

        // 完成 item 拖動
        private void gridView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
        {
            lblMsg.Text += Environment.NewLine;
            lblMsg.Text += $"DropResult: {args.DropResult}";

            _draggingEmployee = null;
        }

        // item 被拖進了 borderDelete
        private void borderDelete_DragEnter(object sender, DragEventArgs e)
        {
            // 關於 DragEventArgs 的詳細介紹,以及其他屬於 UIElement 拖放方面的詳細介紹請參見:/Controls/BaseControl/UIElementDemo/DragDropDemo.xaml

            e.AcceptedOperation = DataPackageOperation.Move;

            e.DragUIOverride.IsGlyphVisible = false;
            e.DragUIOverride.Caption = "松開則刪除";

            Debug.WriteLine("DragEnter");
        }

        // item 被 drop 到了 borderDelete
        private void borderDelete_Drop(object sender, DragEventArgs e)
        {
            // 從數據源中刪除指定的 Employee 對象
            Data1.Remove(_draggingEmployee);
            Data2.Remove(_draggingEmployee);

            _draggingEmployee = null;

            // 在 borderDelete 放下了拖動項
            Debug.WriteLine("Drop");
        }

        // item 被拖出了 borderDelete
        private void borderDelete_DragLeave(object sender, DragEventArgs e)
        {
            Debug.WriteLine("DragLeave");
        }

        // item 在 borderDelete 上面拖動著
        private void borderDelete_DragOver(object sender, DragEventArgs e)
        {
            Debug.WriteLine("DragOver");
        }


        // item 被拖進了 gridView2
        private void gridView2_DragEnter(object sender, DragEventArgs e)
        {
            e.AcceptedOperation = DataPackageOperation.Copy;
        }

        // item 被 drop 到了 gridView2
        private void gridView2_Drop(object sender, DragEventArgs e)
        {
            Data2.Add(_draggingEmployee);
        }


        // lblEmployee 被按下了
        private async void lblEmployee_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            // 啟動 lblEmployee 的拖動操作
            await lblEmployee.StartDragAsync(e.GetCurrentPoint(lblEmployee));
        }

        // lblEmployee 開始被拖動
        private void lblEmployee_DragStarting(UIElement sender, DragStartingEventArgs args)
        {
            args.Data.RequestedOperation = DataPackageOperation.Copy;

            _draggingEmployee = Employee;
        }


        // 通過 api 將 gridView1 中的第 1 項數據移動到第 3 項數據的位置
        private void buttonMove_Click(object sender, RoutedEventArgs e)
        {
            // 利用數據源 ObservableCollection 的 Move 方法
            Data1.Move(0, 2);
        }
    }
}



OK
[源碼下載]

背水一戰 Windows 10 (56) - 控件(集合類): ListViewBase - 基礎知識, 拖動項