空談録

世界で5人くらいに役立ちたい

Windows Store Appsで階層式メニューを考えるやつ その3

消化試合(バグバグ)

というわけで最終回です。あどべんとする気0
今回は3枚目以降から一つ前に戻る実装を作ります

前回の記事その1

Windows Store Appsで階層式メニューを考える - 空談録

その2

Windows Store Appsで階層式メニューを考えるやつの途中経過 - 空談録

親のViewModelの参照

その2に書きましたが、Stackなどで管理するのとプロパティ持つかの2択かなと
Stackだと順序が保持できるのでListなどよりはいいです

今回はプロパティ持たせます
TransitionPaneViewModelにParentViewModelというTransitionPaneViewModelのプロパティを追加しときます。こっちは特にバインドしないので自動実装のままでいいかなと

通常の遷移を行うためのMainPage.transiteOperateを次のようにしておきます

private void transitionOperate(PaneItemViewModel pvm)
{
    if (_viewModel.MainObject == null)
    {
        _viewModel.MainObject = pvm.ChildViewModel;
        _viewModel.MainObject.ParentViewModel = _viewModel.PastObject;
    }
    else
    {
        var cvm = _viewModel.MainObject;
        _viewModel.MainObject = pvm.ChildViewModel;
        _viewModel.MainObject.ParentViewModel = cvm;
        _viewModel.MovingObject = cvm;
    }
}

初回の動作の関係上PastObjectではなくMainObjectに追加された時点で参照を追加していきます
まあ追加できるタイミングで追加していけばよいかなと

ここまでやったらVisualStateを追加してみます

VisualStateとアニメーションの追加

TransitionBackというVisualStateを追加しておきます
このTransitionBackはTransittedの状態とまったく同じです

追加したらTransitionBack→TransittedMainへの遷移アニメーションを追加します
Transittedへの遷移とはまったく逆の動きをさせれば完了です

VisualTransitionのxamlはこんな感じ

<VisualTransition From="TransitionBack" GeneratedDuration="0:0:0.5" To="TransittedMain">
    <Storyboard Completed="Storyboard_Completed">
		<DoubleAnimation Duration="0" To="300" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="PastObjectGrid" d:IsOptimized="True"/>
		<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="PastObjectGrid">
			<DiscreteObjectKeyFrame KeyTime="0">
				<DiscreteObjectKeyFrame.Value>
					<Thickness>-200,0,0,0</Thickness>
				</DiscreteObjectKeyFrame.Value>
			</DiscreteObjectKeyFrame>
		</ObjectAnimationUsingKeyFrames>
		<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="MovingObjectGrid">
			<DiscreteObjectKeyFrame KeyTime="0">
				<DiscreteObjectKeyFrame.Value>
					<Thickness>-200,0,0,0</Thickness>
				</DiscreteObjectKeyFrame.Value>
			</DiscreteObjectKeyFrame>
		</ObjectAnimationUsingKeyFrames>
		<DoubleAnimation Duration="0:0:0.5" To="300" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="MovingObjectGrid" d:IsOptimized="True">
			<DoubleAnimation.EasingFunction>
				<ExponentialEase EasingMode="EaseInOut" Exponent="9"/>
			</DoubleAnimation.EasingFunction>
		</DoubleAnimation>
		<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="MovingObjectGrid">
			<DiscreteObjectKeyFrame KeyTime="0">
				<DiscreteObjectKeyFrame.Value>
					<Visibility>Visible</Visibility>
				</DiscreteObjectKeyFrame.Value>
			</DiscreteObjectKeyFrame>
			<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
				<DiscreteObjectKeyFrame.Value>
					<Visibility>Visible</Visibility>
				</DiscreteObjectKeyFrame.Value>
			</DiscreteObjectKeyFrame>
		</ObjectAnimationUsingKeyFrames>
	</Storyboard>
</VisualTransition>

えっ?見覚えがあるって? まあ確認のためにも

xaml側はこんな感じでいいのでまたコード側に戻ります。あとちょっとです

逆方向への遷移を行う

前回MenuBackのイベントを追加していた気がするので、そのあたりはすでに終わったとしときます
あっ、ちなみにtransittedCountはMenuBackのあたりでやると管理方法がずれてきます

まずMovingObjectGridのDataContextChangedから変更していきます
VisualStateを2回変更して遷移アニメーションを実行します

if (args.NewValue != null)
{
    if (isBackTransition)
    {
        if (VisualStateManager.GoToState(this, "TransitionBack", false))
        {
            VisualStateManager.GoToState(this, "TransittedMain", true);
        }
    }
    else
    {
        isTransitting = true;
        transittedCount += 1;
        VisualStateManager.GoToState(this, "Transitted", true);
    }
}

内部状態管理が複雑化してきました
VisualStateManager.GoToStateの戻り値を取得することで非同期的な操作から同期的に操作されるようにしておきます

Storyboard_CompletedのisBackTransitionのあたりを変更します。transittedCountはここで減らしておきます

else if (isBackTransition)
{
    isBackTransition = false;
    if (transittedCount == 1)
    {
        MainPane_MoveCompleted(MenuAnimationKind.MoveFirst);
    }
    else
    {
        transittedCount -= 1;
        MainPane_MoveCompleted(MenuAnimationKind.MovePrevious);
    }
}

これでMenuHostControlの中身の編集は終了です。MainPageのほうを変更していきます

MenuBackイベントのハンドラを追加していきます。ParentViewModelを使ってさくさくとやります

private void MainMenuControl_MenuBack()
{
    var pvm = _viewModel.PastObject;
    _viewModel.PastObject = pvm.ParentViewModel;
    _viewModel.MovingObject = pvm;
}

やってることは簡単で前のを移すだけです

最後にMoveCompletedのswitchのところにMovePreviousの条件のときの動作を追加します

case MenuHostControl.MenuAnimationKind.MovePrevious:
    _viewModel.MainObject = _viewModel.MovingObject;
    _viewModel.MovingObject = null;
    break;

これで動くはず…?

なんとなく動画とってみた
https://portalvhdsrp3qt9v47nzbn.blob.core.windows.net/publicphoto/ScreenCapture_2014-12-27%2022.31.27.mp4
Azure Storage直置きです。動作読めません
サイズ2.9MB
PCから見ると幸せかもしれません


というわけで長かったあどべんと記事完です
ダウンロードは次のURLからでもどうぞ
https://portalvhdsrp3qt9v47nzbn.blob.core.windows.net/publicphoto/SwitchableLeftMenuApp.zip

結局ソースコード合わせて23000文字くらい?
一日で書けるあれじゃなかった…

今年は最後にすうぇー検証してまとめ記事書いて終わりたい

この辺で