扩展GridView控件——为内容项添加拖放及分组功能
引言
相信大家對GridView都不陌生,是非常有用的控件,用于平鋪有序的顯示多個內容項。打開任何WinRT應用或者是微軟合作商的網站,都會在APP中發現GridView的使用。“Tiles”提供了一個簡單易用,平鋪方式來組織內容顯示。Windows8的開始菜單是最典型的GridView 示例。“開始菜單”顯示了系統中安裝的所有應用程序,而且支持重新排列。
本文源于我們項目的開發人員,他們想在項目中提供與GridView相同的用戶體驗,想要創建類GridView控件。
GridView 可以顯示大小不定的內容項,并且以有序的方式顯示。如果各個內容項無序,并且內容尺寸大小相同,GirdView還支持拖拽操作。然而,這些功能并不是默認提供的,需要編寫一定的代碼才能實現。
本文主要介紹了擴展GridView控件——稱為GridViewEx, GridViewEx主要實現GridView在不同大小的內容項中的拖拽功能。
背景
首先了解GridView的基本屬性和功能,GridView包含一些屬性集和 ItemTemplate。為了實現通過拖拽操作執行重排列功能,必須完成以下三件事:
1. 設置AllowDrop屬性為true;
2. 設置CanReorderItems 屬性值為True;
3. 綁定數據源,該數據源必須支持數據修改或支持重排序。例如,使用ObservableCollection或IList數據源。
<GridView ItemsSource="{Binding}" AllowDrop="True" CanReorderItems="True"><GridView.ItemTemplate><DataTemplate><Border BorderBrush="Aqua" BorderThickness="1" Background="Peru"><Grid Margin="12"><Grid.RowDefinitions><RowDefinition /><RowDefinition Height="Auto"/></Grid.RowDefinitions><TextBlock Text="{Binding}"/><TextBlock Grid.Row="1">item</TextBlock></Grid></Border></DataTemplate></GridView.ItemTemplate> </GridView>擴展后的GridView使用拖拽操作將會非常方便快捷。
GridViewEx 控件
GridViewEx控件彌補了GridView,功能如下:
- 實現Item Panel 拖拽操作,而不是WrapGrid,StackPanel、VirtualizingStackPanel等
- 分組時,實現拖拽功能;
我們也為GridViewEx增加了新建分組的功能,如果用戶將內容項拖到控件左邊或右邊時會觸發新建分組操作。
實現拖拽代碼:
1: public class GridViewEx : GridView 2: { 3: /// <summary> 4: /// Initializes a new instance of the <see cref="GridViewEx"/> control. 5: /// </summary> 6: public GridViewEx() 7: { 8: // see attached sample 9: } 10: 11: private void GridViewEx_DragItemsStarting(object sender, DragItemsStartingEventArgs e) 12: { 13: // see attached sample 14: } 15: 16: /// <summary> 17: /// Stores dragged items into DragEventArgs.Data.Properties["Items"] value. 18: /// Override this method to set custom drag data if you need to. 19: /// </summary> 20: protected virtual void OnDragStarting(DragItemsStartingEventArgs e) 21: { 22: // see attached sample 23: }該控件包含幾個變量,用來存儲拖放內容的索引。OnDragStarting 事件在DragEventArgs.Data.Properties[“Items”] 中存儲拖拽的內容。OnDragStarting 需要根據自己的需求重寫。
當用戶拖拽某一項內容時,需要給用戶提示來引導用戶將內容放在合適的位置上。標準的GriView對象是通過滑動相鄰的內實項來實現的。本文將在GridViewEx中完善此操作。
1: /// <summary> 2: /// Shows reoder hints while custom dragging. 3: /// </summary> 4: protected override void OnDragOver(DragEventArgs e) 5: { 6: // see attached sample } 7: ? 8: private int GetDragOverIndex(DragEventArgs e) 9: { 10: // see attached sample 11: }實現拖放代碼
首先需要重寫GridView.OnDrop方法,該方法會當用戶釋放某一項內容時觸發。重寫Ondrop方法,代碼如下:
1: /// <summary> 2: /// Handles drag and drop for cases when it is not supported by the Windows.UI.Xaml.Controls.GridView control 3: /// </summary> 4: protected override async void OnDrop(DragEventArgs e) 5: { 6: // see attached sample 7: }?
OnDrop方法主要實現了內容項從源分組移到目標分組的邏輯代碼,以及創建新分組的功能。
添加新分組
如果GrideView通過將IsSourceGrouped值為True來綁定CollectionViewSource情況下,GridView提供分組功能,這就意味著分組必須對數據源進行分組,但GridView沒有訪問數據的權限。因此本文在執行拖放操作時,實現添加新分組功能。GridViewEx.BeforeDrop事件處理此需求,并且提供更多的數據信息,如DragEventArgs數據。
當用戶執行拖放操作時,觸發BeforeDrop 事件。
1: /// <summary> 2: /// Occurs before performing drop operation, 3: /// </summary> 4: public event EventHandler<BeforeDropItemsEventArgs> BeforeDrop; 5: /// <summary> 6: /// Rises the <see cref="BeforeDrop"/> event. 7: /// </summary> 8: /// <param name="e">Event data for the event.</param> 9: protected virtual void OnBeforeDrop(BeforeDropItemsEventArgs e) 10: { 11: // see attached sample 12: }BeforeDropItemEventArgs包含關于被拖拽的內容項的重要信息,該信息在OnDrop事件中可使用的。
1: /// <summary> 2: /// Provides data for the <see cref="GridViewEx.BeforeDrop"/> event. 3: /// </summary> 4: public sealed class BeforeDropItemsEventArgs : System.ComponentModel.CancelEventArgs 5: { 6: /// <summary> 7: /// Gets the item which is being dragged. 8: /// </summary> 9: public object Item 10: { 11: get; 12: } 13: /// <summary> 14: /// Gets the current item index in the underlying data source. 15: /// </summary> 16: public int OldIndex 17: { 18: get; 19: } 20: /// <summary> 21: /// Gets the index in the underlying data source where 22: /// the item will be inserted by the drop operation. 23: /// </summary> 24: public int NewIndex 25: { 26: get; 27: } 28: /// <summary> 29: /// Gets the bool value determining whether end-user actions requested 30: /// creation of the new group in the underlying data source. 31: /// This property only makes sense if GridViewEx.IsGrouping property is true. 32: /// </summary> 33: /// <remarks> 34: /// If this property is true, create the new data group and insert it into 35: /// the groups collection at the positions, specified by the 36: /// <see cref="BeforeDropItemsEventArgs.NewGroupIndex"/> property value. 37: /// Then the <see cref="GridViewEx"/> will insert dragged item 38: /// into the newly added group. 39: /// </remarks> 40: public bool RequestCreateNewGroup 41: { 42: get; 43: } 44: /// <summary> 45: /// Gets the current item data group index in the underlying data source. 46: /// This property only makes sense if GridViewEx.IsGrouping property is true. 47: /// </summary> 48: public int OldGroupIndex 49: { 50: get; 51: } 52: /// <summary> 53: /// Gets the data group index in the underlying data source 54: /// where the item will be inserted by the drop operation. 55: /// This property only makes sense if GridViewEx.IsGrouping property is true. 56: /// </summary> 57: public int NewGroupIndex 58: { 59: get; 60: } 61: /// <summary> 62: /// Gets the original <see cref="DragEventArgs"/> data. 63: /// </summary> 64: public DragEventArgs DragEventArgs 65: { 66: get; 67: } 68: }AllowNewGroup屬性確定用戶拖拽某一內容項到控件邊界時,是否創建新組。GridView并沒有提供此功能,在GridViewEX添加此功能。
1: /// <summary> 2: /// Gets or sets the value determining whether new group should be created at 3: /// dragging the item to the empty space. 4: /// </summary> 5: public bool AllowNewGroup 6: { 7: get { return (bool)GetValue(AllowNewGroupProperty); } 8: set { SetValue(AllowNewGroupProperty, value); } 9: } 10: 11: /// <summary> 12: /// Identifies the <see cref="AllowNewGroup"/> dependency property. 13: /// </summary> 14: public static readonly DependencyProperty AllowNewGroupProperty = 15: DependencyProperty.Register("AllowNewGroup", typeof(bool), 16: typeof(GridViewEx), new PropertyMetadata(false));為了在拖拽過程中添加分組,需要將AllowNewGroup屬性設置為True。處理GridViewEx.BeforeDrop事件,該事件的參數能夠幫助決定單項內容的起始位置和目的位置。在BeforeDrop事件的Handler中,使用 NewGroupIndex 創建新的數據組,并插入到已有組集合。最后,需要實現的擴展GridView控件模板。在用戶可拖拽的項目的位置創建新分組,并使用占位符來代替。一旦用戶拖某一內容放置到控件的邊界時,觸發創建新分組,ItemsPresenter的兩個邊界元素是新組的占位符。
GridViewEx控件模板generic.xaml,如下:
1: <Style TargetType="local:GridViewEx"> 2: <Setter Property="Padding" Value="0,0,0,10" /> 3: <Setter Property="IsTabStop" Value="False" /> 4: <Setter Property="TabNavigation" Value="Once" /> 5: <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> 6: <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/> 7: <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" /> 8: <Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" /> 9: <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" /> 10: <Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="False" /> 11: <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" /> 12: <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" /> 13: <Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" /> 14: <Setter Property="IsSwipeEnabled" Value="True" /> 15: <Setter Property="Template"> 16: <Setter.Value> 17: <ControlTemplate TargetType="local:GridViewEx"> 18: <Border BorderBrush="{TemplateBinding BorderBrush}" 19: Background="{TemplateBinding Background}" 20: BorderThickness="{TemplateBinding BorderThickness}"> 21: <ScrollViewer x:Name="ScrollViewer" 22: TabNavigation="{TemplateBinding TabNavigation}" 23: HorizontalScrollMode=" 24: {TemplateBinding ScrollViewer.HorizontalScrollMode}" 25: HorizontalScrollBarVisibility= 26: "{TemplateBinding 27: ScrollViewer.HorizontalScrollBarVisibility}" 28: IsHorizontalScrollChainingEnabled= 29: "{TemplateBinding 30: ScrollViewer.IsHorizontalScrollChainingEnabled}" 31: VerticalScrollMode=" 32: {TemplateBinding ScrollViewer.VerticalScrollMode}" 33: VerticalScrollBarVisibility= 34: "{TemplateBinding 35: ScrollViewer.VerticalScrollBarVisibility}" 36: IsVerticalScrollChainingEnabled= 37: "{TemplateBinding 38: ScrollViewer.IsVerticalScrollChainingEnabled}" 39: IsHorizontalRailEnabled=" 40: {TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" 41: IsVerticalRailEnabled=" 42: {TemplateBinding ScrollViewer.IsVerticalRailEnabled}" 43: ZoomMode="{TemplateBinding 44: ScrollViewer.ZoomMode}" 45: IsDeferredScrollingEnabled=" 46: {TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" 47: BringIntoViewOnFocusChange=" 48: {TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"> 49: <StackPanel Orientation="Horizontal"> 50: <Border Width="60" 51: x:Name="NewGroupPlaceHolderFirst" 52: Background="Transparent" 53: Padding="{TemplateBinding Padding}" 54: Visibility="{Binding AllowNewGroup, 55: Converter={StaticResource 56: VisibilityConverter}, 57: RelativeSource={RelativeSource TemplatedParent}}"/> 58: <ItemsPresenter 59: Header="{TemplateBinding Header}" 60: HeaderTemplate="{TemplateBinding HeaderTemplate}" 61: HeaderTransitions="{TemplateBinding HeaderTransitions}" 62: Padding="{TemplateBinding Padding}"/> 63: <Border Width="60" 64: x:Name="NewGroupPlaceHolderLast" 65: Background="Transparent" 66: Padding="{TemplateBinding Padding}" 67: Visibility="{Binding AllowNewGroup, 68: Converter={StaticResource 69: VisibilityConverter}, 70: RelativeSource={RelativeSource TemplatedParent}}"/> 71: </StackPanel> 72: </ScrollViewer> 73: </Border> 74: </ControlTemplate> 75: </Setter.Value> 76: </Setter> 77: </Style>?
豐富GridViewEx功能
如上所示,我們已經實現了基本的拖拽操作,與Windows8 開始菜單類似的功能,接下來討論如何實現以下功能:
- 尺寸大小不定的內容項
- 分組
- 添加新組
- 節省跨會話布局空間
大小不定的內容項
Windows8展示了不同大小的Tiles,但是目前GridView或GridViewEx還不支持此功能。因為GridView使用WrapGrid作為默認的ItemsPanel,WrapPanel只能創建一種布局,即所有的條目尺寸相同的。因此微軟提供了VariableSizedWrapGrid,支持不同大小塊的布局創建。
GridViewEx控件的優勢在于能夠使用VariableSizedWrapGrid,并且很好的支持拖放操作。為了使用VariableSizedWrapGrid 并顯示不同大小的內容項,必須實現以下功能:
將GridViewEx.ItemsPanel 設置為VariableSizedWrapGrid
在GridView中重寫GridView 的PrepareContainerForItemOverride 方法。在該方法中,可以設置Item的RowSpan或ColumnSpan屬性來識別內容項的大小。
即生成繼承GridViewEx的新控件MyGridView。為什么需要擴展GridViewEx控件而不是重寫GridViewEx的PrepareContainerForItemOverride方法?因為指定Item尺寸的邏輯必須放在數據模型中,而不是控件內部。
如想將某一項顯示較大一點,需要在數據項中創建一個屬性返回比1大的整型數值,來設置RowSpanhuoColumnSpan屬性。
1: public class Item 2: { 3: public int Id { get; set; } 4: public int ItemSize { get; set; } 5: /* */ 6: }因此,當創建新的內容項,我們要指定ItemSize屬性。如果值為1則表明常規尺寸,如果值為2則表明大尺寸,ColumnSpan屬性則設置為2。
1: /// <summary> 2: /// This class sets VariableSizedWrapGrid.ColumnSpanProperty for GridViewItem controls, 3: /// so that every item can have different size in the VariableSizedWrapGrid. 4: /// </summary> 5: public class MyGridView : GridViewSamples.Controls.GridViewEx 6: { 7: // set ColumnSpan according to the business logic 8: // (maybe some GridViewSamples.Samples.Item or group properties) 9: protected override void PrepareContainerForItemOverride( 10: Windows.UI.Xaml.DependencyObject element, object item) 11: { 12: try 13: { 14: GridViewSamples.Samples.Item it = item as GridViewSamples.Samples.Item; 15: if (it != null) 16: { 17: element.SetValue( 18: Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, it.ItemSize); 19: } 20: } 21: catch 22: { 23: element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, 1); 24: } 25: finally 26: { 27: base.PrepareContainerForItemOverride(element, item); 28: } 29: } 30: }創建MyGridView實例,并綁定到數據集合。
1: <local:MyGridView AllowDrop="True" CanReorderItems="True" 2: CanDragItems="True" IsSwipeEnabled="True" 3: ItemsSource="{Binding}" 4: ItemTemplate="{StaticResource ItemTemplate}" > 5: <GridView.ItemsPanel> 6: <ItemsPanelTemplate> 7: <VariableSizedWrapGrid ItemHeight="160" 8: ItemWidth="160" /> 9: </ItemsPanelTemplate> 10: </GridView.ItemsPanel> 11: <GridView.ItemContainerStyle> 12: <Style TargetType="GridViewItem"> 13: <Setter Property="HorizontalContentAlignment" 14: Value="Stretch"/> 15: <Setter Property="VerticalContentAlignment" 16: Value="Stretch"/> 17: </Style> 18: </GridView.ItemContainerStyle> 19: </local:MyGridView>如上所示,我們將指定內容項的ItemSize屬性設置為2,效果如圖所示:
分組
使用GridViewEx控件,能夠實現添加新分組和拖拽等功能,也是在App中最為常見的功能,實現分組必須完成以下設置:
- 為GridView綁定CollectionViewSource,必須使用支持分組的數據源。CollectionViewSource可視為代理服務器。
- 使用GroupStyle確定分組結果如何顯示,GroupStyle包含Header Tempate及Panel,因此需要指定子項目的排序方式。
在GridViewEx中添加支持不同大小的內容項,邏輯代碼:
1: <local:MyGridView AllowDrop="True" CanReorderItems="True" 2: CanDragItems="True" IsSwipeEnabled="True" 3: ItemsSource="{Binding}" 4: ItemTemplate="{StaticResource ItemTemplate}" > 5: <GridView.ItemsPanel> 6: <ItemsPanelTemplate> 7: <VirtualizingStackPanel Orientation="Horizontal"/> 8: </ItemsPanelTemplate> 9: </GridView.ItemsPanel> 10: <GridView.GroupStyle> 11: <GroupStyle> 12: <GroupStyle.HeaderTemplate> 13: <DataTemplate> 14: <Grid Background="LightGray" 15: Margin="0"> 16: <TextBlock Foreground="Black" 17: Margin="10" 18: Style="{StaticResource 19: GroupHeaderTextStyle}"> 20: <Run Text="{Binding Id}"/> 21: <Run Text=" group"/> 22: </TextBlock> 23: </Grid> 24: </DataTemplate> 25: </GroupStyle.HeaderTemplate> 26: ? 27: <GroupStyle.ContainerStyle> 28: <Style TargetType="GroupItem"> 29: <Setter Property="BorderBrush" 30: Value="DarkGray"/> 31: <Setter Property="BorderThickness" 32: Value="2"/> 33: <Setter Property="Margin" 34: Value="3,0"/> 35: </Style> 36: </GroupStyle.ContainerStyle> 37: ? 38: <GroupStyle.Panel> 39: <ItemsPanelTemplate> 40: <VariableSizedWrapGrid ItemHeight="160" 41: ItemWidth="160" /> 42: </ItemsPanelTemplate> 43: </GroupStyle.Panel> 44: </GroupStyle> 45: </GridView.GroupStyle> 46: ? 47: <GridView.ItemContainerStyle> 48: <Style TargetType="GridViewItem"> 49: <Setter Property="HorizontalContentAlignment" 50: Value="Stretch"/> 51: <Setter Property="VerticalContentAlignment" 52: Value="Stretch"/> 53: </Style> 54: </GridView.ItemContainerStyle> 55: </local:MyGridView>運行演示:
添加新分組
自定義的GridViewEx控件支持新分組的創建,因此需要設置AllowNewGroup為True。其次處理添加新分組的數據層,處理GridViewEx.BeforeDrop 事件。
1: /// <summary> 2: /// Creates new CollectionViewSource and updates page DataContext. 3: /// </summary> 4: private void UpdateDataContext() 5: { 6: CollectionViewSource source = new CollectionViewSource(); 7: source.Source = _groups; 8: source.ItemsPath = new PropertyPath("Items"); 9: source.IsSourceGrouped = true; 10: this.DataContext = source; 11: } 12: // creates new group in the data source, 13: // if end-user drags item to the new group placeholder 14: private void MyGridView_BeforeDrop(object sender, Controls.BeforeDropItemsEventArgs e) 15: { 16: if (e.RequestCreateNewGroup) 17: { 18: // create new group and re-assign datasource 19: Group group = Group.GetNewGroup(); 20: if (e.NewGroupIndex == 0) 21: { 22: _groups.Insert(0, group); 23: } 24: else 25: { 26: _groups.Add(group); 27: } 28: UpdateDataContext(); 29: } 30: }也可以使用Drop事件刪除空分組
1: // removes empty groups (except the last one) 2: private void MyGridView_Drop(object sender, DragEventArgs e) 3: { 4: bool needReset = false; 5: for (int i = _groups.Count - 1; i >= 0; i--) 6: { 7: if (_groups[i].Items.Count == 0 && _groups.Count > 1) 8: { 9: _groups.RemoveAt(i); 10: needReset = true; 11: } 12: } 13: if (needReset) 14: { 15: UpdateDataContext(); 16: } 17: }節省布局空間
Windows8支持掛起或終止功能,為了提供更好的用戶體驗,我們繼續改善此前實現的功能,當用戶離開當前頁面,將當前的布局暫存。在本示例中,我們使用JSON 字符串簡化數據序列化。根據已有的數據、數據的大小及需求,以其他格式來保存數據。我們主要將“業務對象集合”保存。
為了節省布局空間。重寫LayoutAwarePage方法:
1: /// <summary> 2: /// Populates the page with content passed during navigation. Any saved state is also 3: /// provided when recreating a page from a prior session. 4: /// </summary> 5: /// <param name="navigationParameter">The parameter value passed to 6: /// <see cref="Frame.Navigate(Type, 7: /// Object)"/> when this page was initially requested. 8: /// </param> 9: /// <param name="pageState" 10: /// >A dictionary of state preserved by this page during an earlier 11: /// session. This will be null the first time a page is visited.</param> 12: protected override void LoadState(Object navigationParameter, 13: Dictionary<String, Object> pageState) 14: { 15: base.LoadState(navigationParameter, pageState); 16: if (pageState != null && pageState.Count > 0 17: && pageState.ContainsKey("Groups")) 18: { 19: // restore groups and items from the previously serialized state 20: System.Runtime.Serialization.Json.DataContractJsonSerializer rootSer = 21: new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(List<Group>)); 22: var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes 23: ((string)pageState["Groups"])); 24: _groups = (List<Group>)rootSer.ReadObject(stream); 25: } 26: else 27: { 28: // if we get here for the first time and don't have 29: // serialized content, fill groups and items from scratch 30: for (int j = 1; j <= 12; j++) 31: { 32: Group group = Group.GetNewGroup(); 33: for (int i = 1; i <= 7 + j % 3; i++) 34: { 35: group.Items.Add(new Item() 36: { 37: Id = i, 38: GroupId = group.Id 39: }); 40: } 41: _groups.Add(group); 42: } 43: } 44: UpdateDataContext(); 45: } 46: ? 47: /// <summary> 48: /// Preserves state associated with this page in case the application is suspended or the 49: /// page is discarded from the navigation cache. Values must conform to the serialization 50: /// requirements of <see cref="SuspensionManager.SessionState"/>. 51: /// </summary> 52: /// <param name="pageState"> 53: /// An empty dictionary to be populated with serializable state.</param> 54: protected override void SaveState(Dictionary<String, Object> pageState) 55: { 56: // save groups and items to JSON string so that 57: // it's possible to restore page state later 58: base.SaveState(pageState); 59: System.Runtime.Serialization.Json.DataContractJsonSerializer rootSer = 60: new System.Runtime.Serialization.Json.DataContractJsonSerializer 61: (typeof(List<Group>)); 62: var stream = new MemoryStream(); 63: rootSer.WriteObject(stream, _groups); 64: string str = System.Text.Encoding.UTF8.GetString(stream.ToArray(), 65: 0, (int)stream.Length); 66: pageState.Add("Groups", str); 67: } 68: ? 69: /// <summary> 70: /// Invoked when this page is about to be displayed in a Frame. 71: /// </summary> 72: /// <param name="e">Event data that describes 73: /// how this page was reached. The Parameter 74: /// property is typically used to configure the page.</param> 75: protected override void OnNavigatedTo(NavigationEventArgs e) 76: { 77: // restore page state 78: var frameState = 79: GridViewSamples.Common.SuspensionManager.SessionStateForFrame(this.Frame); 80: if (frameState.ContainsKey("TilePageData")) 81: { 82: this.LoadState(e.Parameter, 83: (Dictionary<String, Object>)frameState["TilePageData"]); 84: } 85: else 86: { 87: this.LoadState(e.Parameter, null); 88: } 89: } 90: ? 91: protected override void OnNavigatedFrom(NavigationEventArgs e) 92: { 93: // save page state with "TilePageData" key 94: var frameState = 95: GridViewSamples.Common.SuspensionManager.SessionStateForFrame(this.Frame); 96: var pageState = new Dictionary<String, Object>(); 97: this.SaveState(pageState); 98: frameState["TilePageData"] = pageState; 99: }總結
GridViewEx控件豐富了GirdView控件功能,改進了基礎功能,提升用戶體驗。到此已經實現了GridView項與Windows8開始菜單具有的相同用戶體驗,如果你想了解如何在Windows10平臺下開發UWP引用,請持續關注下篇文章:如何在Windows10中開發UWP應用
除了?GirdView 以外,具備觸摸和鍵盤導航操作的自動或手動平鋪布局的控件還有ComponentOne TileControl for WinForms,它不但提供自適應Windows8的樣式布局,還具有類似Windows8風格的交互體驗和靈活便捷的定制能力。
原文鏈接:http://www.codeproject.com/Articles/536519/Extending-GridView-with-Drag-and-Drop-for-Grouping
總結
以上是生活随笔為你收集整理的扩展GridView控件——为内容项添加拖放及分组功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库内容导出为excel并下载
- 下一篇: 过来人经验!聊聊前端工程师的职业规划