wpf 具有最小化动画的自定义窗口样式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21418160/
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
Custom window style with minimize animation
提问by pastillman
I wanted to have a customized window so followed a few tutorials which enable this by setting the window style to none, and then adding the title-bar/restore/minimize/close buttons yourself. The minimize is achieved by simply handling the click event and setting the Window-state to minimized, but this doesn't show the minimize animation you see on Windows 7, and just instantly hides the window, which feels very odd when used with other windows that do animate, as you tend to feel the application is closing.
我想要一个自定义的窗口,所以遵循了一些教程,通过将窗口样式设置为无,然后自己添加标题栏/恢复/最小化/关闭按钮来启用此功能。最小化是通过简单地处理点击事件并将Window-state设置为最小化来实现的,但这并没有显示你在Windows 7上看到的最小化动画,只是立即隐藏了窗口,与其他窗口一起使用时感觉很奇怪做动画,因为你往往觉得应用程序正在关闭。
So, is there anyway of enabling that animation? .. it seems to be disabled when you change the WindowStyle to none.
那么,无论如何都可以启用该动画吗?.. 当您将 WindowStyle 更改为 none 时,它似乎被禁用。
Edit : Test code
编辑:测试代码
public partial class MainWindow : Window
{
public MainWindow()
{
WindowStyle = WindowStyle.None;
InitializeComponent();
}
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
// this doesnt seem to animate
SendMessage(new WindowInteropHelper(this).Handle, 0x0112, (IntPtr)0xF020, IntPtr.Zero);
}
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
base.OnMouseRightButtonDown(e);
WindowStyle = WindowStyle.SingleBorderWindow;
WindowState = WindowState.Minimized;
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => WindowStyle = WindowStyle.None));
}
}
采纳答案by Fayilt
Edited the answer after experimenting a bit.
稍微尝试后编辑了答案。
There are two options: 1.You can change the Style just before minimising and activating the window:
有两个选项: 1.您可以在最小化和激活窗口之前更改样式:
private void Button_OnClick(object sender, RoutedEventArgs e)
{
//change the WindowStyle to single border just before minimising it
this.WindowStyle = WindowStyle.SingleBorderWindow;
this.WindowState = WindowState.Minimized;
}
private void MainWindow_OnActivated(object sender, EventArgs e)
{
//change the WindowStyle back to None, but only after the Window has been activated
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => WindowStyle = WindowStyle.None));
}
This solution has one limitation - it doesn't animate the window if you minimise it from the taskbar.
此解决方案有一个限制 - 如果您从任务栏中将其最小化,它不会为窗口设置动画。
2.Minimise the Window by sending it WM_SYSCOMMAND message with SC_MINIMIZE parameter and changing the border style by hooking into the message (HwndSource.FromHwnd(m_hWnd).AddHook(WindowProc)).
2.通过发送带有 SC_MINIMIZE 参数的 WM_SYSCOMMAND 消息并通过挂钩到消息 ( HwndSource.FromHwnd(m_hWnd).AddHook(WindowProc))更改边框样式来最小化窗口。
internal class ApiCodes
{
public const int SC_RESTORE = 0xF120;
public const int SC_MINIMIZE = 0xF020;
public const int WM_SYSCOMMAND = 0x0112;
}
private IntPtr hWnd;
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
hWnd = new WindowInteropHelper(this).Handle;
HwndSource.FromHwnd(hWnd).AddHook(WindowProc);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SendMessage(hWnd, ApiCodes.WM_SYSCOMMAND, new IntPtr(ApiCodes.SC_MINIMIZE), IntPtr.Zero);
}
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == ApiCodes.WM_SYSCOMMAND)
{
if (wParam.ToInt32() == ApiCodes.SC_MINIMIZE)
{
WindowStyle = WindowStyle.SingleBorderWindow;
WindowState = WindowState.Minimized;
handled = true;
}
else if (wParam.ToInt32() == ApiCodes.SC_RESTORE)
{
WindowState = WindowState.Normal;
WindowStyle = WindowStyle.None;
handled = true;
}
}
return IntPtr.Zero;
}
Neither of the above methods are great, because they are just hacks. The biggest downside is that you can actually see the border reappearing for a moment when you click the button. I'd like to see what others come up with as I don't consider this as a good answer myself.
上述方法都不是很好,因为它们只是黑客。最大的缺点是当您单击按钮时,您实际上可以看到边框重新出现片刻。我想看看其他人提出了什么,因为我自己并不认为这是一个好的答案。
回答by bwing
A newer feature of .NET has solved this problem. Leave your WindowStyle="SingleBorder" or "ThreeDBorder" Leave ResizeMode="CanResize"
.NET 的一个新特性解决了这个问题。保留您的 WindowStyle="SingleBorder" 或 "ThreeDBorder" 保留 ResizeMode="CanResize"
Then add this to the xaml inside the
然后把这个加到里面的xaml
<Window>
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="0" CornerRadius="0" CaptionHeight="0" UseAeroCaptionButtons="False" ResizeBorderThickness="7"/>
</WindowChrome.WindowChrome>
</Window>
The window will not have any of the default border, but will still allow resizing and will not cover the task bar when maximized.It will also show the minimize animation as before.
窗口将没有任何默认边框,但仍允许调整大小,并且在最大化时不会覆盖任务栏。它还将像以前一样显示最小化动画。
EDIT
编辑
This also works for WindowStyle="None". It allows resizing of a borderless window as if it had a border. It also allows you to set AllowsTransparency="True" on the window.
这也适用于 WindowStyle="None"。它允许调整无边框窗口的大小,就像它有边框一样。它还允许您在窗口上设置 AllowsTransparency="True"。
回答by rookie1024
If you handle the WM_NCCALCSIZEmessage by returning 0, handle the WM_NCHITTESTmessage using either your own code (if you want to do manual hit-testing) or also returning 0, and set the WindowStyle to SingleBorder, the window will function like a borderless window but it will have the animations enabled.
如果您WM_NCCALCSIZE通过返回 0 来处理WM_NCHITTEST消息,请使用您自己的代码(如果您想进行手动命中测试)或也返回 0 来处理消息,并将 WindowStyle 设置为 SingleBorder,则该窗口将像无边框窗口一样运行将启用动画。
If completely necessary, you may also need to handle the WM_GETMINMAXINFOto fix the maximize size - it clips the borders off because the window's style is SingleBorder.
如果完全有必要,您可能还需要处理WM_GETMINMAXINFO以修复最大化尺寸 - 因为窗口的样式是 SingleBorder,所以它会剪掉边框。
回答by TheSkilluminati
I have found another solution, if you need AllowTransparency = True. It is not beautiful, rather a bit hacky. But it is very simple and works great. This uses a empty Window, which is shortly shown when you Minimize/Maximize/Restore your Window, and it has the same position, widht, size and height as your Window. It always has the same Window State as your Window, and it does the animations, which YourWindow lacks because of WindowStyle None and AllowTransparency True. The empty Window has a Window Style SingleBorderWindow and AllowTransparency = false. (by default, so i dont need to set it manually) This is a must or it would not animate. After it has animated, it is completely hidden. You could adjust the look of the Fake Window (BackgroundColor etc...) to YourWindow if it doesnt look good.
如果您需要 AllowTransparency = True,我找到了另一种解决方案。它并不漂亮,而是有点hacky。但它非常简单并且效果很好。这使用了一个空窗口,当您最小化/最大化/恢复您的窗口时会很快显示它,它与您的窗口具有相同的位置、宽度、大小和高度。它始终具有与您的 Window 相同的 Window State,并且它执行由于 WindowStyle None 和 AllowTransparency True 而 YourWindow 缺少的动画。空窗口具有窗口样式 SingleBorderWindow 和 AllowTransparency = false。(默认情况下,所以我不需要手动设置)这是必须的,否则它不会动画。动画完成后,它是完全隐藏的。如果看起来不好看,您可以将假窗口(BackgroundColor 等...)的外观调整为 YourWindow。
public partial Class YourWindowClass : Window
{
Window w;
public YourWindowClass()
{
InitializeComponent();
w = new Window();
w.Width = Width;
w.Height = Height;
w.WindowStartupLocation = this.WindowStartupLocation;
}
Then, you place this in your state changed event:
然后,你把它放在你的状态改变事件中:
private void YourWindowClass_StateChanged(object sender, EventArgs e)
{
w.Left = Left;
w.Top = Top;
w.Width = Width;
w.Height = Height;
w.Show();
if (WindowState == WindowState.Minimized)
{
if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
w.WindowState = WindowState.Minimized;
CloseWindow();
}
if (WindowState == WindowState.Normal)
{
w.WindowState = WindowState.Normal;
w.Left = this.Left;
Activate();
CloseWindow();
}
if (WindowState == WindowState.Maximized)
{
w.WindowState = WindowState.Maximized;
Activate();
CloseWindow();
}
}
Finally, create this async Task in YourWindowClass. It will wait shortly and then hide the extra Window.
最后,在 YourWindowClass 中创建这个异步任务。它会稍等片刻,然后隐藏额外的窗口。
public async Task CloseWindow()
{
await Task.Delay(600);
w.Visibility = Visibility.Hidden;
}
This will remove the hidden hack Window, so if you close the real Window, the hacky animation Window will close too. Else it wouldnt be Visible to the user because its hidden, but it will still be open and so parts of your App are open. This is a behaviour we dont want, so put this as your Closed Event:
这将移除隐藏的 hack Window,因此如果您关闭真实的 Window,那么 hacky 动画 Window 也会关闭。否则它不会对用户可见,因为它是隐藏的,但它仍然是打开的,所以你的应用程序的一部分是打开的。这是我们不想要的行为,所以把它作为你的封闭事件:
private void YourWindowClass_Closed(object sender, EventArgs e)
{
w.Close();
}

