I am populating a ListView programmatically when the user selects an entry from a ComboBox.
I'd like to improve the app usability by allowing the user to swipe left/right as an alternative way to change the content.
More exactly when the user performs a flick or short swipe, the listview gets populated with a different set of items. I don't need any visual cue that a manipulation has started (i.e. Like Edge does when using gestures to navigate back and forth)
Below is the simplified XAML structure.
<Grid
x:Name="MainGrid"
ManipulationMode="TranslateX, TranslateInertia"
Loaded="MainGrid_Loaded">
<ComboBox x:Name="CBMonthPicker" />
<ListView
x:Name="LV"
ItemsSource="{Binding CalItems, ElementName=page}"
DataContext="MainPage"
IsItemClickEnabled="True"
ItemClick="OnItemClick"
ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">
</ListView>
</Grid>
Here is the code I am using to detect swipe, which works on Desktop (if I simulate a swipe with the mouse), but does not work on mobile (Grid_ManipulationDelta is not triggered on Mobile):
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
MainGrid.ManipulationDelta += Grid_ManipulationDelta;
}
private void Grid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var swiped = e.Cumulative.Translation.X;
var swipeTriggerThreshhold = 300;
//if the gesture was not above a set threshold we ignore it
if (!e.IsInertial || swiped * swiped < swipeTriggerThreshhold * swipeTriggerThreshhold )
return;
//If we got so far, we registered a swipe
e.Complete(); //end manipulation so we only handle it once
if (swiped < 0) { //we swiped left
changeDisplayedMonth(-1);
}
else{ //we swiped right
changeDisplayedMonth(1);
}
}
My guess is that ListView or ListViewItem(s) are somehow hiding the manipulation from the Grid.
I have tried using the ListView and ListViewItem to handle the manipulation, but ManipulationDelta is never triggered for any of them, even if I set ManipulationMode to TranslateX, TranslateInertia.
There are plenty of questions on SO regarding swipe for ListViewItem (like featured in Outlook). This question is not related to that.
Because the content is loaded dynamically based on user input, I'd rather not use Pivot. Even if I could, it would mean significant changes to my interface, which again I'd rather not do.
After a lot of fiddling around with Pivot which yielded completely unsatisfactory results, I have discovered FlipView which could be used with a bit of hacking to accomplish exactly what I wanted.
The trick is to add two dummy FlipViewItems, one before the one containing our ListView, and one after it. The next step is to capture the SelectionChanged event of the FlipView and if the FlipViewItem before our content has been selected, trigger an action as you would for a back swipe, the same for the FlipViewItem after our content. The last step is to force selection back to our content ListViewItem.
A small annoyance with FlipView hacked in this manner is that, when forcing selection back to our middle FlipViewItem (the one hosting our content), the animation is opposite to the one expected by the end user. This is easily fixed by setting the property UseTouchAnimationsForAllNavigation="False" on the FlipView.
Bonus: To show that something is actually happening while repopulating the ListView, I have added a progress ring. For this to work, it is important that the FlipView and FlipViewItems and the ListView have their Background Property set to Transparent. And the ListViewItems have a specified background to cover it. This is a totally inelegant solution for implementing a ProgressRing, however it works and it was fast to come up with :).
So this is how the updated XAML code looks like:
<Grid x:Name="MainGrid" >
<ComboBox x:Name="CBMonthPicker" Grid.Row="0"/>
<ProgressRing Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" IsActive="True" />
<FlipView x:Name="MainView" Grid.Row="1"
Loaded="MainView_Loaded"
UseTouchAnimationsForAllNavigation="False"
Background="Transparent">
<FlipViewItem Background="Transparent">
</FlipViewItem>
<FlipViewItem Background="Transparent">
<ListView x:Name="LV"
ItemsSource="{Binding CalItems, ElementName=page}" DataContext="MainPage"
IsItemClickEnabled="True" ItemClick="OnItemClick"
ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">
</ListView>
</FlipViewItem>
<FlipViewItem Background="Transparent">
</FlipViewItem>
</Grid>
And this is how the code behind looks like:
private void MainView_Loaded(object sender, RoutedEventArgs e)
{
MainView.SelectedIndex = 1;
MainView.SelectionChanged += MainView_SelectionChanged;
}
private void MainView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MainView.SelectedIndex == 0)
{
//perform the action for Back Swipe Gesture
changeDisplayedMonth(-1);
//force selection back to our content FlipViewItem
MainView.SelectedIndex = 1;
}
if (MainView.SelectedIndex == 2)
{
//perform the action for Forward Swipe Gesture
changeDisplayedMonth(1);
//force selection back to our content FlipViewItem
MainView.SelectedIndex = 1;
}
}