3

I have a grid, which used as container. Grid consist of UserControl, each one has 600px height and 800px width. I want to make slide animation like presentation by switching visible controls. Here is my xaml code of mainWindow:

<Window x:Class="MessengerWindowsClient.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:MessengerWindowsClient"
    xmlns:pages="clr-namespace:MessengerWindowsClient.Pages"
    mc:Ignorable="d"
    Title="MainWindow" Height="600" Width="800" Closed="Window_Closed">
<Window.Background>
    <ImageBrush ImageSource="Resources/background.jpg"></ImageBrush>
</Window.Background>
<Grid x:Name="Container" RenderTransformOrigin="0.5,0.5" SizeChanged="Container_SizeChanged">
    <pages:WelcomePage x:Name ="WelcomePage" Visibility="Visible" RegisterPage="{Binding ElementName=RegisterPage}" LoginPage="{Binding ElementName=LoginPage}"/>
    <pages:MessagesPage Visibility="Collapsed"/>
    <pages:LoginPage x:Name="LoginPage" Visibility="Collapsed" WelcomePage="{Binding ElementName=WelcomePage}"/>
    <pages:RegisterPage x:Name="RegisterPage" Visibility="Collapsed" WelcomePage="{Binding ElementName=WelcomePage}"/>
</Grid>

Here is code behind:

public partial class MainWindow : Window
{
    private ServiceManager _serviceManager;
    private UIElement _currentPage;

    public MainWindow()
    {
        InitializeComponent();
        _currentPage = this.Container.Children[0];
        this.RegisterPage.RegisterReady += RegisterUser;
        this.RegisterPage.ChangePage += ChangePage;
        this.WelcomePage.ChangePage += ChangePage;
        this.LoginPage.ChangePage += ChangePage;
        _serviceManager = new ServiceManager();
    }

    private void ChangePage(object sender, ChangePageEventArgs e)
    {
        switch (e.Direction)
        {
            case ChangePageDirection.Forward:
                AnimationManager.AnimateForwardPage(e.NewPage, e.OldPage, Container, this.ActualWidth);
                break;
            case ChangePageDirection.Backward:
                AnimationManager.AnimateBackwardPage(e.NewPage, e.OldPage, Container, this.ActualWidth);
                break;
        }
    }

    private async void RegisterUser(object sender, RegisterEventArgs e)
    {
        var isSucceed = await _serviceManager.RegisterUser(e.Name, e.Username, e.Password.ToString(), e.Email);
        e.Password.Dispose();
    }

    private void Window_Closed(object sender, EventArgs e)
    {
        _serviceManager.Dispose();
    }

    private void Container_SizeChanged(object sender, SizeChangedEventArgs e)
    {

            UpdateLayout();
        }
    }
}

I tried to use this.ActualWidth, but it gives value that is more than my display resolution. So part of my control goes behind the screen. And after the animation completes it returns back. Using any width property of grid gives wrong value, even with UpdateLayout() on resize event.

Edit: Screenshots

After animation completed and after _container.HorizontalAlignment = HorizontalAlignment.Stretch;.

Viacheslav
  • 43
  • 7

1 Answers1

0

Are you trying to animate the width or height of a certain UI element? You need to make a custom animation class that extends AnimationTimeline and define an animation inside a Storyboard in XAML. You will need to create a custom class GridLengthAnimation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Animation;

namespace Infrastructure.Animations
{
    public class GridLengthAnimation : AnimationTimeline
    {
        protected override Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public override Type TargetPropertyType => typeof(GridLength);

        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength),
                typeof(GridLengthAnimation));
        }

        public static readonly DependencyProperty FromProperty;
        public GridLength From
        {
            get => (GridLength)GetValue(GridLengthAnimation.FromProperty);
            set => SetValue(GridLengthAnimation.FromProperty, value);
        }

        public static readonly DependencyProperty ToProperty;
        public GridLength To
        {
            get => (GridLength)GetValue(GridLengthAnimation.ToProperty);
            set => SetValue(GridLengthAnimation.ToProperty, value);
        }

        public override object GetCurrentValue(object defaultOriginValue,
    object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;
            if (fromVal > toVal)
            {
                return new GridLength((1 - animationClock.CurrentProgress.Value) *
                    (fromVal - toVal) + toVal, GridUnitType.Pixel);
            }
            else
            {
                return new GridLength(animationClock.CurrentProgress.Value *
                    (toVal - fromVal) + fromVal, GridUnitType.Pixel);
            }
        }
    }
}

You can then use this in XAML inside a Storyboard like this:

<Storyboard x:Key="storyboardName">
            <support:GridLengthAnimation Storyboard.TargetName="YourElementToAnimate" Storyboard.TargetProperty="Width" From="{Binding StartAnimationWidth}" To="{Binding EndAnimationWidth}" DecelerationRatio="0.9" Duration="0:0:0.6"/>            
</Storyboard>

The StartAnimationWidth and EndAnimationWidth are properties of type GridLength and are defined in ViewModel

private GridLength _endAnimationWidth = new GridLength(100);
public GridLength EndAnimationWidth
{
    get => _endAnimationWidth;
    set => SetProperty(ref _endAnimationWidth,value);
}

You can then trigger the animation from the code behind:

Storyboard sb = Resources["storyboardName"] as Storyboard;
sb.Begin();
AndrejH
  • 2,028
  • 1
  • 11
  • 23