こんな感じの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 { publicsealedpartialclass SwipeControl : UserControl { privateconststring NormalState = "NormalVisualState"; privateconststring LeftState = "LeftVisualState"; privateconststring 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); } privatevoid 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で綺麗に区切る感じじゃなくて、もっと泥臭いコードが必要そう。要検討。