2012-09-08 11 views
5

Mi piacerebbe animare la larghezza e l'altezza di una finestra wpf. Ho provato quanto segue, che sfortunatamente anima solo la larghezza ... l'altezza della finestra non cambia mai.Animazione di una larghezza e di un'altezza della finestra WPF

Sono sicuro di aver perso qualcosa di stupido e spero che postando qui qualcuno vedrà il mio errore!

Ecco il codice dietro per una semplice finestra con un pulsante che ho cablato fare per il ridimensionamento:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     this.AnimateWindowSize(ActualWidth + 200, ActualHeight + 200); 
    } 
} 

Ed ecco il codice di animazione che ho scritto come un metodo di estensione in modo che possa essere applicato a qualsiasi finestra ...

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(); 
    } 
} 

Grazie in anticipo per qualsiasi aiuto.

risposta

2

Dopo il commento di Joe sull'uso di proprietà pinvoke e dipendenza, ho finito con questo codice. Mi scuso ora se il codice è lungo e non avrei dovuto metterlo tutto qui. La matematica non è perfetta per le taglie. C'è una bella differenza tra il WPF Actual (altezza/larghezza) rispetto a Rect.Height/Width, potrebbero essere necessari alcuni calcoli per ottenere le dimensioni esatte desiderate.

Questo è stato aggiunto alla 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); 
} 

/// <summary> 
/// SetWindowPos Flags 
/// </summary> 
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; 
} 

E nel codice del PO ho cambiato le proprietà di altezza e di destinazione larghezza di conseguenza

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

a

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

risposta originale:

Da quello che ho trovato non ci sono problemi con il tuo codice. Quando ho cambiato l'ordine in cui aggiungevo le animazioni all'istanza dello storyboard (sb.Children.Add), ho ottenuto l'animazione dell'altezza senza larghezza.

Questo mi porta a credere che mentre la prima animazione sta accadendo, l'altra animazione non è più valida.

Tutto ciò che ho potuto inventare è animarli uno dopo l'altro facendo in modo che un'animazione sia leggermente più lunga dell'altra. L'animazione più lunga si verificherà una volta completata la prima animazione.

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)))); 

Neanche con gli storyboard XAML è possibile ridimensionare contemporaneamente l'altezza e la larghezza della finestra.

+0

Questo è quello che ho trovato anche io ... Mi chiedevo se ci potesse essere una soluzione usando paralleltimelines, ma non potevo farlo funzionare. Questo http://stackoverflow.com/questions/1769317/animate-window-resize-width-and-height-c-sharp-wpf?rq=1 sembra un trucco ma forse questo è l'unico modo per farlo funzionare ?? –

+0

Sì, sembra che potrebbe essere necessario eseguire manualmente. Quel collegamento era una buona scoperta. –

+1

Alcune di queste proprietà di livello superiore su Window sono un po 'strane e non si comportano allo stesso modo delle altre proprietà WPF perché sono al limite di Win32. Se si desidera animarli insieme, un modo WPFy per farlo sarebbe creare una DependencyProperty sulla finestra che sotto le copertine p/invoca SetWindowPos sul supporto HwndSource. Non ho un ambiente di sviluppo di Windows disponibile per scrivere la soluzione, quindi inserendo questo come commento invece di una risposta. Se lo fai su WPF pre-4.0 ci sono altri problemi di cui preoccuparti. Scriverò una chiacchierata se ne avrò presto una possibilità. –

0

L'unico suggerimento con DP più recenti mi è sembrato un po 'eccessivo, soprattutto perché la soluzione ha riconosciuto che non sarebbe stata ridimensionata contemporaneamente. Nella mia rapida sperimentazione, l'aggiunta di un ritardo (tramite Task.Run()) di 1ms pari ha raggiunto il risultato finale (ridimensionamento della finestra). Anche questa soluzione non viene ridimensionata contemporaneamente e quindi l'animazione non è elegante come potrebbe essere, ma alla fine "funziona".

Problemi correlati