自定义窗口样式,最小化动画

时间:2014-01-28 22:21:13

标签: wpf animation styles minimize

我想要一个自定义窗口,所以遵循一些教程,通过将窗口样式设置为none,然后自己添加标题栏/恢复/最小化/关闭按钮来启用此功能。通过简单地处理click事件并将Window-state设置为最小化来实现最小化,但是这并不显示您在Windows 7上看到的最小化动画,并且只是立即隐藏窗口,当使用时感觉非常奇怪其他做动画的窗口,因为你倾向于觉得应用程序正在关闭。

那么,有没有启用动画? ..当您将WindowStyle更改为none时,它似乎被禁用。

编辑:测试代码

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

4 个答案:

答案 0 :(得分:7)

在尝试了一下之后编辑了答案。

有两种选择: 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));
}

此解决方案有一个限制 - 如果您从任务栏中将其最小化,则不会为窗口设置动画。

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

上述两种方法都不是很好,因为它们只是黑客攻击。最大的缺点是,当您单击按钮时,您实际上可以看到边框重新出现。我想看看其他人提出了什么,因为我不认为这是一个很好的答案。

答案 1 :(得分:6)

.NET的新功能解决了这个问题。 保留WindowStyle =“SingleBorder”或“ThreeDBorder” 保留ResizeMode =“CanResize”

然后将其添加到

中的xaml
<Window>
  <WindowChrome.WindowChrome>
    <WindowChrome GlassFrameThickness="0" CornerRadius="0" CaptionHeight="0" UseAeroCaptionButtons="False" ResizeBorderThickness="7"/>
  </WindowChrome.WindowChrome>
</Window>

窗口没有任何默认边框,但仍然允许调整大小,并且在最大化时不会覆盖任务栏。它还会像以前一样显示最小化动画。

修改

这也适用于WindowStyle =“None”。它允许调整无边框窗口的大小,就像它有边框一样。它还允许您在窗口上设置AllowTransparency =“True”。

答案 2 :(得分:1)

如果您通过返回0来处理WM_NCCALCSIZE消息,请使用您自己的代码处理WM_NCHITTEST消息(如果您想进行手动命中测试)或者还返回0,并设置WindowStyle对于SingleBorder,窗口将像无边框窗口一样运行,但它将启用动画。

如果完全有必要,您可能还需要处理WM_GETMINMAXINFO来修复最大化大小 - 它会剪切边框,因为窗口的样式是SingleBorder。

答案 3 :(得分:0)

如果您需要AllowTransparency = True,我找到了另一种解决方案。 它不漂亮,而且有点hacky。 但它很简单,效果很好。这使用一个空窗口,当您最小化/最大化/恢复窗口时,它会立即显示,并且它与窗口具有相同的位置,宽度,大小和高度。它总是和你的窗口具有相同的窗口状态,并且它会执行动画,因为WindowStyle None和AllowTransparency True,因此YouWindow缺少这些动画。空窗口具有Window Style 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;           
    }

然后,您将其置于状态更改事件中:

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

最后,在YourWindowClass中创建此异步任务。它会很快等待,然后隐藏额外的窗口。

    public async Task CloseWindow()
    {
        await Task.Delay(600);
        w.Visibility = Visibility.Hidden;
    }

这将删除隐藏的黑客窗口,因此如果你关闭真实的窗口,hacky动画窗口也会关闭。否则它对用户来说是不可见的,因为它是隐藏的,但它仍然是打开的,所以你的应用程序的部分是开放的。这是我们不想要的行为,所以把它作为你的封闭事件:

    private void YourWindowClass_Closed(object sender, EventArgs e)
    {
        w.Close();
    }