背水一戰 Windows 10 之 控制項(集合類 - 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
[源碼下載]