【问题标题】:Custom window style with minimize animation具有最小化动画的自定义窗口样式
【发布时间】:2014-02-20 11:50:24
【问题描述】:

我想要一个自定义的窗口,所以遵循了一些教程,通过将窗口样式设置为无,然后自己添加标题栏/恢复/最小化/关闭按钮来实现这一点。最小化是通过简单处理点击事件并将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));
    }
}

【问题讨论】:

    标签: wpf animation styles minimize


    【解决方案1】:

    .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" 时,它仍然会禁用动画并覆盖任务栏。因此,如果您尝试制作透明窗口,则此方法不起作用。

    【讨论】:

    • 如果您将 AllowsTransparency 设置为 true 并且 WindowStyle = none,则最小化动画将消失。
    • @TheMuffinMan 我似乎遇到了和你一样的问题,需要一个透明的窗口,但是,甜美的动画。你找到其他方法了吗?
    • 这根本没有回答问题!
    • @FrankZ。这将创建一个无边框窗口,在最小化时仍显示动画。我错过了问题的另一部分吗?
    • @FrankZ。您对 WindowStyle=None 是正确的。调整大小仍然有效,但动画确实中断了。我已经更正了我的答案。
    【解决方案2】:

    在尝试了一下之后修改了答案。

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

    上述方法都不是很好,因为它们只是黑客。最大的缺点是,当您单击按钮时,您实际上可以看到边框再次出现。我想看看其他人的想法,因为我自己认为这不是一个好的答案。

    【讨论】:

    • 谢谢。我能够获得第二种工作方法,但不是第一种。第一个仍然没有显示任何动画。我用我使用的代码编辑了我的帖子
    • 我已经更新了我的答案。我错了。在这两种情况下,当您单击按钮时,您都会注意到标题重新出现。但是当您自己发送消息时,窗口在最小化时始终是动画的。如果你只是改变按钮点击的边框,当你通过任务栏最小化它时它不会动画。
    • 很抱歉这么晚才回来,我想这已经足够了。除非你有非常复杂的内容和延迟渲染,否则它并不是那么明显
    • 在你的第一个黑客作品中,但不幸的后果是在恢复窗口时导致窗口大小错误。
    • 我很好奇为什么没有人知道 windows 是如何正常处理这个问题的,所以我们可以修改源代码并修改它以适应我们的情况。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      如果您需要 AllowTransparency = True,我已经找到了另一种解决方案。 它并不漂亮,而是有点hacky。 但它非常简单并且效果很好。这使用了一个空窗口,当您最小化/最大化/恢复您的窗口时会立即显示它,它与您的窗口具有相同的位置、宽度、大小和高度。它始终具有与您的 Window 相同的 Window State,并且它会执行 YourWindow 由于 WindowStyle None 和 AllowTransparency True 而缺少的动画。空窗口具有窗口样式 SingleBorderWindow 和 AllowTransparency = false。 (默认情况下,所以我不需要手动设置)这是必须的,否则它不会动画。动画完成后,它是完全隐藏的。如果看起来不好,您可以将假窗口(背景颜色等)的外观调整为 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;
          }
      

      这会移除隐藏的hack Window,所以如果你关闭真正的Window,hacky 动画Window 也会关闭。否则它对用户来说是不可见的,因为它是隐藏的,但它仍然是打开的,所以你的应用程序的一部分是打开的。这是我们不想要的行为,所以把它作为你的 Closed Event:

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

      【讨论】:

      • 这根本不起作用。主窗口显示在假窗口完成动画之前。
      猜你喜欢
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多