こんな感じのUserControlを用意します。
<UserControl x:Class="App26.SwipeControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App26" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" ManipulationMode="TranslateX,System"> <Grid x:Name="Root" Background="White"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="VisualStateGroup"> <VisualState x:Name="NormalVisualState"> <Storyboard> <DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Left" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Right" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="LeftVisualState"> <Storyboard> <DoubleAnimation Duration="0:0:0.5" To="100" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Left" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Right" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> <VisualState x:Name="RightVisualState"> <Storyboard> <DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Left" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation Duration="0:0:0.5" To="100" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="Right" EnableDependentAnimation="True"> <DoubleAnimation.EasingFunction> <CircleEase EasingMode="EaseOut" /> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Rectangle x:Name="Left" Grid.Column="0" Fill="Red" Width="0"> </Rectangle> <Rectangle x:Name="Right" Grid.Column="2" Fill="Green" Width="0"> </Rectangle> <TextBlock Grid.Column="1" Text="Hello world" Style="{ThemeResource BodyTextBlockStyle}" Foreground="Black"/> </Grid> </UserControl>
アニメーションの定義が長いけど、幅を100にしたり0にしたり状態に応じて調整してるだけです。DoubleAnimationだけなので、よく見るとシンプル。UserControlのContentには左右のメニューがわりにRectangleを置いています。
あとは、コードビハインドでさくっとManipulationDeltaイベントを処理してやるだけ。
using Reactive.Bindings.Extensions; using System; using System.Linq; using System.Reactive.Linq; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; // ユーザー コントロールのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=234236 を参照してください namespace App26 { public sealed partial class SwipeControl : UserControl { private const string NormalState = "NormalVisualState"; private const string LeftState = "LeftVisualState"; private const string RightState = "RightVisualState"; public SwipeControl() { this.InitializeComponent(); VisualStateManager.GoToState(this, NormalState, true); Observable.FromEvent<ManipulationDeltaEventHandler, ManipulationDeltaRoutedEventArgs>( x => (_, e) => x(e), x => this.ManipulationDelta += x, x => this.ManipulationDelta -= x) .Where(x => Math.Abs(x.Delta.Translation.X) >= 10) .Throttle(TimeSpan.FromMilliseconds(100)) .ObserveOnUIDispatcher() .Subscribe(this.SwipeExecute); } private void SwipeExecute(ManipulationDeltaRoutedEventArgs e) { var currentState = VisualStateManager.GetVisualStateGroups(this.Root) .FirstOrDefault(x => x.Name == "VisualStateGroup") ?.CurrentState ?.Name ?? NormalState; switch (currentState) { case NormalState: currentState = e.Delta.Translation.X < 0 ? RightState : LeftState; break; case LeftState: currentState = e.Delta.Translation.X < 0 ? NormalState : LeftState; break; case RightState: currentState = e.Delta.Translation.X < 0 ? RightState : NormalState; break; } VisualStateManager.GoToState(this, currentState, true); } } }
(RxとReactiveProperty使ってます。)
やってることは、現在のVisualStateと左右のどちらに指が移動したのかを見て次のVisualStateを決めているだけです。
実行すると初期状態はこんな感じ。
右にスワイプするとこんな風になる(アニメーションしながらにょきっと出てくる)
課題
指に追随する形で左右のメニューが出てくるようにするには、VisualStateで綺麗に区切る感じじゃなくて、もっと泥臭いコードが必要そう。要検討。