动画 WPF 窗口的宽度和高度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12332385/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Animating a WPF window width and height
提问by Cameron Peters
I'd like to animate the width and height of a wpf window. I've tried the following, which unfortunately just animates the width... the height of the window never changes.
我想为 wpf 窗口的宽度和高度设置动画。我尝试了以下方法,不幸的是,它只是为宽度设置了动画……窗口的高度永远不会改变。
I'm sure I've missed something silly and hope that by posting here someone will see my error!
我确定我错过了一些愚蠢的东西,希望通过在这里发布有人会看到我的错误!
Here's the code behind for a simple window with a button I've wired up do to the resize:
这是一个简单的窗口背后的代码,我已经连接了一个按钮来调整大小:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.AnimateWindowSize(ActualWidth + 200, ActualHeight + 200);
}
}
And here is the animation code I've written as an extension method so it could be applied to any window...
这是我作为扩展方法编写的动画代码,因此它可以应用于任何窗口......
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();
}
}
Thanks in advance for any help.
在此先感谢您的帮助。
回答by Shane Charles
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.
在 Joe 关于使用 pinvoke 和依赖属性的评论之后,我最终得到了这段代码。如果代码很长,我现在道歉,我不应该把它全部放在这里。尺寸的数学计算并不完美。WPF Actual(Height/Width) 与 Rect.Height/Width 之间存在很大差异,可能需要进行一些计算才能获得所需的确切尺寸。
This was added to the MainWindow class
这被添加到 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;
}
And in the OP's code I changed the height and width target properties accordingly
在 OP 的代码中,我相应地更改了高度和宽度目标属性
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.
从我发现的情况来看,您的代码没有问题。当我更改将动画添加到故事板 (sb.Children.Add) 实例的顺序时,我得到了没有宽度的高度动画。
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.
即使使用 XAML 故事板,我也无法同时调整窗口的高度和宽度。
回答by Tim Heuer
The one suggestion with newer DPs seemed a bit overkill for me, especially since the solution acknowledged that it still wouldn't resize simultaneously. In my quick experimentation, adding a delay (via Task.Run()) of even 1ms achieved the final result (window resizes). This solution also does not resize simultaneously and so the animation isn't as elegant as it could be, but it 'works' in the end.
新 DP 的一个建议对我来说似乎有点矫枉过正,特别是因为该解决方案承认它仍然不会同时调整大小。在我的快速实验中,添加一个甚至 1ms 的延迟(通过 Task.Run())达到了最终结果(窗口调整大小)。此解决方案也不会同时调整大小,因此动画并不像它应有的那样优雅,但最终它“有效”。

