Filtering and Sorting a ListView in a UWP – Part 1 – The UX

2015-10-28 14:01:50来源:作者:Adrian Hall人点击

In my last article, I got the ListView that is driving my task list hooked up to my Task Store by making my Task Store an ObservableCollection. Today I am starting work on the UX for implementing filtering. I want two activities: I want to filter the tasks based on their completed status and I want to be able to sort tasks on something – title, since that’s the only thing I have.

To do this, I need an application bar. Right now, I have a placeholder piece of XML in there. It’s the fourth segment of my UI. Starting from the top, I have the title, the new task box, the list of tasks and finally the app bar. Appbars are relatively easy to produce:

<AppBar Grid.Row="3" Background="LightGray"><StackPanel Orientation="Horizontal"><AppBarButton Label="Filter"><AppBarButton.Icon><SymbolIcon Symbol="Filter"/></AppBarButton.Icon></AppBarButton><AppBarButton Label="Sort"><AppBarButton.Icon><SymbolIcon Symbol="Sort"/></AppBarButton.Icon></AppBarButton></StackPanel></AppBar>

One of the things I try to do is to expand the XAML out. It can always be consolidated later, but breaking it out when you need to is sometimes hard. As a result, this is a longer form than I normally do. If you run this, you will get a light gray bottom panel with an ellipsis on it. Pressing the ellipsis will expand the app bar and show two items on the left – a sort and filter icon. Since I’m using the symbol font, the standard icons are being used.

When I press these icons, I want a menu, but let me think about the menu for a moment. There are two types of menu – a standard flyout allows you to construct the menu yourself with a stack panel and any valid XAML. There is also a MenuFlyout for handling menus. There are MenuFlyoutItem types (a standard one, a separator and a toggle) that can be used to construct the menu. The difference is what happens when a menu item is selected. In the standard flyout, the menu remains open. In the MenuFlyout, the menu closes. If you want the menu to remain open, you must produce your own XAML code. If you are ok with it being closed automatically, then you can use a MenuFlyout. A MenuFlyout would look like this:

<AppBarButton Label="Filter"><AppBarButton.Icon><SymbolIcon Symbol="Filter"/></AppBarButton.Icon><AppBarButton.Flyout><MenuFlyout><ToggleMenuFlyoutItem x:Name="IncludeCompletedCheckbox" IsChecked="False">Completed</ToggleMenuFlyoutItem></MenuFlyout></AppBarButton.Flyout></AppBarButton>

This menu contains one item. If I click on the Completed icon, it gets a check mark next to it and the menu is closed. This is great for menus that select new pages, but it is really a bad experience for menus that set or clear settings. I want a standard flyout here. Here is my XAML code that includes the flyout:

<AppBar Grid.Row="3" Background="LightGray"><StackPanel Orientation="Horizontal"><AppBarButton Label="Filter"><AppBarButton.Icon><SymbolIcon Symbol="Filter"/></AppBarButton.Icon><AppBarButton.Flyout><Flyout><StackPanel><CheckBox x:Name="IncludeCompletedCheckbox" IsChecked="False" Click="IncludeCompletedCheckbox_Changed">Completed</CheckBox></StackPanel></Flyout></AppBarButton.Flyout></AppBarButton><AppBarButton Label="Sort"><AppBarButton.Icon><SymbolIcon Symbol="Sort"/></AppBarButton.Icon><AppBarButton.Flyout><Flyout><StackPanel Orientation="Vertical"><RadioButton x:Name="SortMethod_Unsorted" GroupName="SortOptions" IsChecked="True" Checked="SortMethod_Changed">Unsorted</RadioButton><RadioButton x:Name="SortMethod_ByTitle" GroupName="SortOptions" Checked="SortMethod_Changed">By Title</RadioButton></StackPanel></Flyout></AppBarButton.Flyout></AppBarButton></StackPanel></AppBar>

Clicking anywhere outside the menu will close the menu in both cases. It’s just a case of the action when a menu option is chosen. In this case, I’ve got a checkbox for the filter and I’ve got a radio button group for the sort mechanism. I’m using the standard styling here (I’ll do another blog post on styling when I work out how to do it), so the controls are clearly form controls.

Now for the code. In MainPage.xaml.cs– the code behind file for the XAML – I’ve defined an enum for the SortMethod:

enum SortMethod { Unsorted, ByTitle }

I’ve also defined two private variables in the MainPagepartial class. The default values reflect the default values in the XAML:

public sealed partial class MainPage : Page { private TaskStore store; private bool includeCompleted = false; private SortMethod sortMethod = SortMethod.Unsorted;

The XAML defines two event handlers I need – one for the checkbox and one for the sort method. In both cases, they set the private variables so that the two values are in sync:

private void IncludeCompletedCheckbox_Changed(object sender, RoutedEventArgs e){this.includeCompleted = (bool)((CheckBox)sender).IsChecked;System.Diagnostics.Debug.WriteLine(string.Format("Include Completed = {0}", this.includeCompleted));}private void SortMethod_Changed(object sender, RoutedEventArgs e){var item = (RadioButton)sender;if (item.Name.Equals("SortMethod_Unsorted")){this.sortMethod = SortMethod.Unsorted;}else if (item.Name.Equals("SortMethod_ByTitle")){this.sortMethod = SortMethod.ByTitle;}System.Diagnostics.Debug.WriteLine(string.Format("Sort Method = {0}", this.sortMethod));}

I could easily have a property that just returns these from the UI, but I think it’s more obvious when reading the code what is happening when I use a private variable intermediary. Right now, all I am doing is to change the private variables. These will be used by the filtered list, but that’s my next blog post. Until then, you can find this code in my GitHub Repository.