After Joe's comment of using pinvoke and dependency properties I ended up with this code. I'll apologize now if the code is long and I shouldn't have put it all here. The math is not perfect on the sizes. There is quite a difference between the WPF Actual(Height/Width) versus the Rect.Height/Width, it may take some calculations to get the exact sizes you want.
This was added to the MainWindow class
[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;
}
And in the OP's code I changed the height and width target properties accordingly
Storyboard.SetTargetProperty(aniHeight, new PropertyPath(Window.HeightProperty));
Storyboard.SetTargetProperty(aniWidth, new PropertyPath(Window.WidthProperty));
to
Storyboard.SetTargetProperty(aniHeight, new PropertyPath(MainWindow.WindowHeightAnimationProperty));
Storyboard.SetTargetProperty(aniWidth, new PropertyPath(MainWindow.WindowWidthAnimationProperty));
Original answer:
From what I have found there is no problem with your code. When I changed the order in which I was adding the animations to the storyboard (sb.Children.Add) instance, I got the height animating with no width.
This leads me to believe that while the first animation is happening, the other animation becomes invalid.
All I could come up with is having them animate one after the other by having one animation be slightly longer than the other. The longer animation will occur once the first animation completed.
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))));
Not even using XAML storyboards could I get both height and width of the window to resize simultaneously.