[WPF] Grid.ColumnDefinition の Width をアニメーションで動かす

Grid を GridSplitter を使って分割レイアウト表示することがあります。
この分割表示をアニメーションで動作する方法を記載します。

 

 

 

1. Grid.ColumnDefinition の Width をアニメーションで動かす

[概要] とりあえずやってみましょう。

Grid.ColumnDefinition をアニメーションするには単純に DoubleAnimation で実現することはできないようです。
ここではインターネットを調査して見つけたプログラムをまねして、AnimationTimeline の派生クラスを使うことで実現しています。
この手法はいろいろと応用できそうなので、こちらのプログラミングアイテムに記録を残すことにしました。

作成するプログラムは下図の通りです。
・画面中央のボタンをクリックすることで、画面レイアウトがアニメーションにより開閉します。
・ボタンの矢印も逆方向を向きます。

 

左側パネルを開いた図

 

左側パネルを閉じた図

 

[動画] 動作例です

 

[環境]

コンパイラ : Visual Studio 2015 pro.  
  c#  
フレームワーク: .NET Framework 4.0  
OS : Windows10 home  

 

[プログラムソース]

["MainWindow.xaml"]

<Window x:Class="test_GridSplitter_02.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:test_GridSplitter_02"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" ResizeMode="CanResizeWithGrip">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100*" Name="menuWidth"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="100*"/>
        </Grid.ColumnDefinitions>
        <StackPanel Margin="0"/>
        <GridSplitter x:Name="menuGlidSplitter" HorizontalAlignment="Center" Margin="0" Width="5"
            Grid.Column="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Stretch"/>
        <StackPanel Grid.Column="2" Margin="0">
            <Image Source="P1070769.jpg" VerticalAlignment="Top" Stretch="UniformToFill"/>
        </StackPanel>
        <Button x:Name="btnOpenClose" Content="⇐" Margin="0,0,-15,0" Height="24" HorizontalAlignment="Right"
            Width="24" VerticalContentAlignment="Stretch" FontSize="16" Click="Button_Click"/>
    </Grid>
</Window>

["MainWindow.xaml.cs"]
using System;
using System.Windows;                   // GridLength, ContentElement.BeginAnimation
using System.Windows.Controls;

using System.Windows.Media.Animation;   // EasingFunctionBase, CubicEase


namespace test_GridSplitter_02
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool bMenuOpened = true;
        private GridLength menuWidthOrg;
        private EasingFunctionBase ease = new CubicEase();
        private GridLength fromMenuWidth;
        private GridLength toMenuWidth;


        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var glaMenu = new GridLengthAnimation();

            if ( bMenuOpened == true)
            {
                // menu を閉じる
                bMenuOpened = false;

                // GridSplitter の可動範囲を指定する
                //menuWidth.MinWidth = 0; menuWidth.MaxWidth = 500;

                menuWidthOrg = menuWidth.Width;
                fromMenuWidth = menuWidth.Width;
                toMenuWidth = new GridLength(0.0, GridUnitType.Star);

                // EasingFunction definitions.
                ease.EasingMode = EasingMode.EaseInOut;
                glaMenu.EasingFunction = ease;
            }
            else
            {
                // menu を開く
                bMenuOpened = true;

                // GridSplitter の可動範囲を指定する
                //menuWidth.MinWidth = 0; menuWidth.MaxWidth = 500;

                //fromMenuWidth = new GridLength(menuWidth.ActualWidth);
                fromMenuWidth = menuWidth.Width;
                toMenuWidth = menuWidthOrg;

                // EasingFunction definitions.
                ease.EasingMode = EasingMode.EaseInOut;
                glaMenu.EasingFunction = ease;
            }

            // GridLengthAnimation を設定
            glaMenu.Completed += GlaMenu_Completed;                     // アニメーション完了を通知
            glaMenu.Duration = new Duration(TimeSpan.FromSeconds(0.5)); // 0.5秒 でアニメーション
            //glaMenu.FillBehavior = FillBehavior.HoldEnd;              // FillBehavior の設定は重要。どちらを使う?
            glaMenu.FillBehavior = FillBehavior.Stop;                   // FillBehavior の設定は重要。どちらを使う?
            glaMenu.From = fromMenuWidth;
            glaMenu.To = toMenuWidth;

            // アニメーション開始
            menuWidth.BeginAnimation(ColumnDefinition.WidthProperty, glaMenu);
        }

        private void GlaMenu_Completed(object sender, EventArgs e)
        {
            menuWidth.Width = toMenuWidth;

            if ( bMenuOpened)
            {
                btnOpenClose.Content = "⇐";
                menuGlidSplitter.IsEnabled = true;
            }
            else
            {
                btnOpenClose.Content = "⇒";
                menuGlidSplitter.IsEnabled = false;
            }
        }
    }
}

["GridLengthAimation.cs"]
using System;
using System.Windows;
using System.Windows.Media.Animation;


/// <summary>
/// 下記URLの記事に記載のコードを参考にしました。
/// http://stackoverflow.com/questions/9358025/gridsplitter-not-working-after-applying-an-animation-to-a-grid-column
/// </summary>

namespace test_GridSplitter_02
{
    internal class GridLengthAnimation : AnimationTimeline
    {

        public static readonly DependencyProperty FromProperty;
        public static readonly DependencyProperty ToProperty;
        public static readonly DependencyProperty EasingFunctionProperty;

        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength), typeof(GridLengthAnimation));
            ToProperty = DependencyProperty.Register("To", typeof(GridLength), typeof(GridLengthAnimation));
            EasingFunctionProperty = DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(GridLengthAnimation));
        }

        protected override Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public override Type TargetPropertyType
        {
            get { return typeof(GridLength); }
        }

        public IEasingFunction EasingFunction
        {
            get
            {
                return (IEasingFunction)GetValue(GridLengthAnimation.EasingFunctionProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.EasingFunctionProperty, value);
            }

        }

        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.FromProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.FromProperty, value);
            }
        }

        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.ToProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.ToProperty, value);
            }
        }

        public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromValue = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toValue = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;

            IEasingFunction easingFunction = this.EasingFunction;

            double progress = (easingFunction != null) ? easingFunction.Ease(animationClock.CurrentProgress.Value) : animationClock.CurrentProgress.Value;

            if (fromValue > toValue)
            {
                return new GridLength((1 - progress) * (fromValue - toValue) + toValue, this.To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
            }
            else
            {
                return new GridLength((progress) * (toValue - fromValue) + fromValue, this.To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
            }
        }
    }
}



サンプルプログラム

サンプルプログラム ダウンロード

 

 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

The MIT License (MIT)

Copyright © 2017-2022 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

変更履歴

2022-12-10 - ページデザイン更新
2017-08-15 - 新規作成

 

Programming Items トップページ

プライバシーポリシー