Animation d’une largeur et d’une hauteur de fenêtre WPF

J’aimerais animer la largeur et la hauteur d’une fenêtre wpf. J’ai essayé ce qui suit, qui malheureusement n’anime que la largeur … la hauteur de la fenêtre ne change jamais.

Je suis sûr que j’ai raté quelque chose de stupide et j’espère qu’en publiant ce message, quelqu’un verra mon erreur!

Voici le code derrière pour une simple fenêtre avec un bouton que j’ai câblé faire pour le redimensionner:

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { this.AnimateWindowSize(ActualWidth + 200, ActualHeight + 200); } } 

Et voici le code d’animation que j’ai écrit comme méthode d’extension afin qu’il puisse être appliqué à n’importe quelle fenêtre …

 public static class WindowUtilties { public static void AnimateWindowSize(this Window target, double newWidth, double newHeight) { var sb = new Storyboard {Duration = new Duration(new TimeSpan(0, 0, 0, 0, 200))}; var aniWidth = new DoubleAnimationUsingKeyFrames(); var aniHeight = new DoubleAnimationUsingKeyFrames(); aniWidth.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 200)); aniHeight.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 200)); aniHeight.KeyFrames.Add(new EasingDoubleKeyFrame(target.ActualHeight, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00)))); aniHeight.KeyFrames.Add(new EasingDoubleKeyFrame(newHeight, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 200)))); aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(target.ActualWidth, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00)))); aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(newWidth, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 200)))); Storyboard.SetTarget(aniWidth, target); Storyboard.SetTargetProperty(aniWidth, new PropertyPath(Window.WidthProperty)); Storyboard.SetTarget(aniHeight, target); Storyboard.SetTargetProperty(aniHeight, new PropertyPath(Window.HeightProperty)); sb.Children.Add(aniWidth); sb.Children.Add(aniHeight); sb.Begin(); } } 

Merci d’avance pour votre aide.

Après le commentaire de Joe sur l’utilisation de pinvoke et des propriétés de dépendance, je me suis retrouvé avec ce code. Je m’excuserai maintenant si le code est long et que je n’aurais pas dû tout mettre ici. Le calcul n’est pas parfait sur les tailles. Il existe une grande différence entre les valeurs réelles WPF (hauteur / largeur) et Rect.Hauteur / largeur, des calculs peuvent être nécessaires pour obtenir les tailles exactes souhaitées.

Cela a été ajouté à la classe MainWindow

 [StructLayout(LayoutKind.Sequential)] public struct RECT { public int X; public int Y; public int Width; public int Height; } public enum SpecialWindowHandles { HWND_TOP = 0, HWND_BOTTOM = 1, HWND_TOPMOST = -1, HWND_NOTOPMOST = -2 } [DllImport("user32.dll", SetLastError = true)] static extern bool GetWindowRect(IntPtr hWnd, ref RECT Rect); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); public static readonly DependencyProperty WindowHeightAnimationProperty = DependencyProperty.Register("WindowHeightAnimation", typeof(double), typeof(MainWindow), new PropertyMetadata(OnWindowHeightAnimationChanged)); private static void OnWindowHeightAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window != null) { IntPtr handle = new WindowInteropHelper(window).Handle; var rect = new RECT(); if (GetWindowRect(handle, ref rect)) { rect.X = (int)window.Left; rect.Y = (int)window.Top; rect.Width = (int)window.ActualWidth; rect.Height = (int)(double)e.NewValue; // double casting from object to double to int SetWindowPos(handle, new IntPtr((int)SpecialWindowHandles.HWND_TOP), rect.X, rect.Y, rect.Width, rect.Height, (uint)SWP.SHOWWINDOW); } } } public double WindowHeightAnimation { get { return (double)GetValue(WindowHeightAnimationProperty); } set { SetValue(WindowHeightAnimationProperty, value); } } public static readonly DependencyProperty WindowWidthAnimationProperty = DependencyProperty.Register("WindowWidthAnimation", typeof(double), typeof(MainWindow), new PropertyMetadata(OnWindowWidthAnimationChanged)); private static void OnWindowWidthAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window != null) { IntPtr handle = new WindowInteropHelper(window).Handle; var rect = new RECT(); if (GetWindowRect(handle, ref rect)) { rect.X = (int)window.Left; rect.Y = (int) window.Top; var width = (int)(double)e.NewValue; rect.Width = width; rect.Height = (int) window.ActualHeight; SetWindowPos(handle, new IntPtr((int)SpecialWindowHandles.HWND_TOP), rect.X, rect.Y, rect.Width, rect.Height, (uint)SWP.SHOWWINDOW); } } } public double WindowWidthAnimation { get { return (double)GetValue(WindowWidthAnimationProperty); } set { SetValue(WindowWidthAnimationProperty, value); } } private void GrowClick(object sender, RoutedEventArgs e) { this.AnimateWindowSize(Width+200, Height+200); } ///  /// SetWindowPos Flags ///  public static class SWP { public static readonly int NOSIZE = 0x0001, NOMOVE = 0x0002, NOZORDER = 0x0004, NOREDRAW = 0x0008, NOACTIVATE = 0x0010, DRAWFRAME = 0x0020, FRAMECHANGED = 0x0020, SHOWWINDOW = 0x0040, HIDEWINDOW = 0x0080, NOCOPYBITS = 0x0100, NOOWNERZORDER = 0x0200, NOREPOSITION = 0x0200, NOSENDCHANGING = 0x0400, DEFERERASE = 0x2000, ASYNCWINDOWPOS = 0x4000; } 

Et dans le code de l’OP, j’ai changé les propriétés cibles de hauteur et de largeur en conséquence

 Storyboard.SetTargetProperty(aniHeight, new PropertyPath(Window.HeightProperty)); Storyboard.SetTargetProperty(aniWidth, new PropertyPath(Window.WidthProperty)); 

à

 Storyboard.SetTargetProperty(aniHeight, new PropertyPath(MainWindow.WindowHeightAnimationProperty)); Storyboard.SetTargetProperty(aniWidth, new PropertyPath(MainWindow.WindowWidthAnimationProperty)); 

Réponse originale:

D’après ce que j’ai trouvé, votre code ne pose aucun problème. Lorsque j’ai changé l’ordre dans lequel j’ajoutais les animations à l’instance de storyboard (sb.Children.Add), j’ai eu la hauteur d’animation sans largeur.

Cela me porte à penser que pendant que la première animation est en cours, l’autre animation devient invalide.

Tout ce que je pourrais faire, c’est de les animer les uns après les autres en faisant en sorte qu’une animation soit légèrement plus longue que l’autre. L’animation la plus longue se produira une fois la première animation terminée.

 var sb = new Storyboard { Duration = new Duration(new TimeSpan(0, 0, 0, 0, 300)) }; var aniWidth = new DoubleAnimationUsingKeyFrames(); var aniHeight = new DoubleAnimationUsingKeyFrames(); aniWidth.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 300)); aniHeight.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 150)); aniHeight.KeyFrames.Add(new EasingDoubleKeyFrame(target.ActualHeight, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 00)))); aniHeight.KeyFrames.Add(new EasingDoubleKeyFrame(newHeight, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 150)))); aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(target.ActualWidth, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 150)))); aniWidth.KeyFrames.Add(new EasingDoubleKeyFrame(newWidth, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, 300)))); 

Même en n’utilisant pas de story-boards XAML, la hauteur et la largeur de la fenêtre pouvaient être redimensionnées simultanément.

La seule suggestion faite aux nouveaux PDD me paraissait un peu exagérée, d’autant plus que la solution reconnaissait qu’elle ne redimensionnerait toujours pas simultanément. Lors de ma rapide expérimentation, l’ajout d’un délai (via Task.Run ()) égal à 1 ms a permis d’obtenir le résultat final (redimensionnement de la fenêtre). Cette solution ne redimensionne pas non plus simultanément et l’animation n’est donc pas aussi élégante qu’elle pourrait l’être, mais elle “fonctionne” au final.