1

何ヶ月も悩まされていましたが、最終的にWPFでビュー間を移行する一般的な方法を見つけました。これにより、移行に必要なアニメーションを使用できるようになります。いいえ、VisualStateManagerはここではまったく役に立ちません。同じビュー内のトランジションにのみ適しています。

4

1 に答える 1

1

すでにそこにはフレームワークとテクニックがありますが、率直に言って、それらはすべて私を混乱させたり、不格好に見えたりしました。 別の人の解決策は大丈夫でしたが、私は夜に私を完全に追いつけなかった何らかの理由でそれが本当に好きではありませんでした。たぶん、ほとんどの場合よりも単純ですが、それでも混乱しているように見えたので、その要点を理解して自分で作成しました。しかし、それは私の解決策を刺激しました。

そこで、ヘルパーライブラリをインポートしました。以下は、ビュー間でトランジションを実行するために必要な唯一のコードです。フェードイン/フェードアウトアニメーションだけでなく、任意のアニメーションを使用できます。誰かが私のソリューションをアップロードするのに適した場所を教えてくれたら、喜んで共有します。

コード

赤いボックス(RedGridViewModel)と青いボックス(BlueGridViewModel)のあるウィンドウがあります。各ボックスの下にはボタンがあり、ボックスを現在ではない色に交互に変更します。ボタンを押すと、現在のボックスがフェードアウトし、新しいボックスがフェードインします。トランジションが発生している間、ボタンは無効になり、プログラムをクラッシュさせることなく、両方のボタンをすばやく連続して押すことができます。ここでは2つの従属ViewModelを使用しましたが、必要な数だけ使用できます。

実行中のプログラム

<Window x:Class="ViewModelTransitionTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ViewModelTransitionTest"
    xmlns:custom="clr-namespace:CustomTools;assembly=CustomTools"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{x:Static local:StartingDataContext.DataContext}">
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <!--Reference to my helper library with all the goodies that make this whole thing work somehow-->
            <ResourceDictionary Source="pack://application:,,,/CustomTools;component/ViewModelTransition/TransitionCapableViewModelResources.xaml"/>
            <!--DataTemplates for the Red and Blue view models.  Just tack on 
            the TransitionCapableViewModelStyleWithFadeInAndOut style and you're good to go.-->
            <ResourceDictionary>
                <DataTemplate DataType="{x:Type local:BlueGridViewModel}">
                    <Grid
                        Background="Blue"
                        Opacity="0"
                        Style="{StaticResource TransitionCapableViewModelStyleWithFadeInAndOut}"/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:RedGridViewModel}">
                    <Grid 
                        Background="Red"
                        Opacity="0"
                        Style="{StaticResource TransitionCapableViewModelStyleWithFadeInAndOut}"/>
                </DataTemplate>
            </ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <ContentPresenter 
        Content="{Binding ViewModelA}"
        Grid.Row="0"
        Grid.Column="0"/>
    <ContentPresenter 
        Content="{Binding ViewModelB}"
        Grid.Row="0"
        Grid.Column="1"/>
    <Button
        Content="Change ViewModel"
        Grid.Row="1"
        Grid.Column="0"
        Command="{Binding ChangeViewModelA}"/>
    <Button 
        Content="Change ViewModel"
        Grid.Row="1"
        Grid.Column="1"
        Command="{Binding ChangeViewModelB}"/>
</Grid>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using CustomTools;

namespace ViewModelTransitionTest
{
    /// <summary>
    /// View Model for the main windown.
    /// </summary>
    class MainWindowViewModel : ContainsSwappableViewModelsBase
    {
        /// <summary>
        /// ViewModel properties should always follow this basic format.  You can use a string, 
        /// but I like type-safety and I don't have Visiual Studio 2012, so I use this clunky 
        /// extractPropertyNameMethod to convert the ProeprtyName to a string.
        /// </summary>
        public object ViewModelA
        {
            get
            {
                return viewModels[ExtractPropertyName(() => ViewModelA)];
            }
        }
        public object ViewModelB
        {
            get
            {
                return viewModels[ExtractPropertyName(() => ViewModelB)];
            }
        }

        public TransitionCapableViewModel NewViewModelA
        {
            get
            {
                if (ViewModelA is BlueGridViewModel)
                    return new RedGridViewModel();
                return new BlueGridViewModel();
            }
        }
        public TransitionCapableViewModel NewViewModelB
        {
            get
            {
                if (ViewModelB is BlueGridViewModel)
                    return new RedGridViewModel();
                return new BlueGridViewModel();
            }
        }

        /// <summary>
        /// Each ViewModel property should have a command that changes it.  That command should 
        /// call changeViewModel and check canChangeViewModel as follows.
        /// </summary>
        public ICommand ChangeViewModelA
        {
            get
            {
                return new RelayCommand(
                    x => changeViewModel(() => ViewModelA, NewViewModelA),
                    x => canChangeViewModel(() => ViewModelA, NewViewModelA));
            }
        }
        public ICommand ChangeViewModelB
        {
            get
            {
                return new RelayCommand(
                    x => changeViewModel(() => ViewModelB, NewViewModelB),
                    x => canChangeViewModel(() => ViewModelB, NewViewModelB));
            }
        }

        /// <summary>
        /// In the constructor, you'll want to register each ViewModel property with a starting 
        /// value as follows.  And don't forget to call the base constructor.
        /// </summary>
        /// <param name="viewModelA"></param>
        /// <param name="viewModelB"></param>
        public MainWindowViewModel(object viewModelA, object viewModelB)
            :base()
        {
            addViewModelPropertyAndValueToDictionary(() => ViewModelA, viewModelA);
            addViewModelPropertyAndValueToDictionary(() => ViewModelB, viewModelB);
        }

        /// <summary>
        /// The only method you have to override from the base class is this one which regsiters 
        /// your commands with a corresponding viewmodel so that I can raise the relevant change 
        /// notifications because if you're as bad as me, you'll probably screw it up.
        /// </summary>
        protected override void associateCommandsWithViewModelProperties()
        {
            associateCommandWithViewModelProperty(ExtractPropertyName(() => ChangeViewModelA), ExtractPropertyName(() => ViewModelA));
            associateCommandWithViewModelProperty(ExtractPropertyName(() => ChangeViewModelB), ExtractPropertyName(() => ViewModelB));
        }
    }
}

完全を期すために、これは文字通り、この例のために私が書いた他の唯一のコードです(このスレッドでは少し冗長な非常に小さなヘルパーアセンブリを除く):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CustomTools;

namespace ViewModelTransitionTest
{
    static class StartingDataContext
    {
        public static MainWindowViewModel DataContext { 
            get 
            { 
                return new MainWindowViewModel(new BlueGridViewModel(), new RedGridViewModel()); 
            } 
        }
    }

    class RedGridViewModel : TransitionCapableViewModel
    {
    }

    class BlueGridViewModel : TransitionCapableViewModel
    {
    }
}

私は以下を利用しました:

WPFデータパイプ

元のリンクが見つかりませんが、wpfイベントをVMコマンドにバインドすることが重要でした

コードコントラクトプラグイン、コードコントラクトが好きだから

興味があれば、配管コードを投稿するか、ソリューション全体をどこかにアップロードできます。それほど長くはありません。2つの基本クラスと、2つのスタイルと2つのストーリーボードを含むリソースファイルです。

于 2012-10-31T04:15:12.893 に答える