Пользовательский стиль окна с минимизацией анимации

Я хотел иметь настраиваемое окно, поэтому следовал нескольким руководствам, которые позволяют это сделать, установив для стиля окна значение none, а затем самостоятельно добавив кнопки заголовка / восстановления / минимизации / закрытия. Минимизация достигается простой обработкой события щелчка и установкой состояния окна в минимизированное, но при этом не отображается анимация минимизации, которую вы видите в 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));
    }
}

person pastillman    schedule 28.01.2014    source источник


Ответы (4)


Отредактировал ответ, немного поэкспериментировав.

Есть два варианта: 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. Сверните окно, отправив ему сообщение WM_SYSCOMMAND с параметром SC_MINIMIZE и изменив стиль границы, подключившись к сообщению (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;
}

Ни один из вышеперечисленных методов не хорош, потому что это всего лишь хаки. Самым большим недостатком является то, что вы можете увидеть, как на мгновение снова появляется граница, когда вы нажимаете кнопку. Я бы хотел посмотреть, что придумали другие, потому что я сам не считаю это хорошим ответом.

person Fayilt    schedule 28.01.2014
comment
Спасибо. Мне удалось заставить работать второй подход, но не первый. Первый по-прежнему не показывает анимации. Я отредактировал свое сообщение с кодом, который я использую - person pastillman; 31.01.2014
comment
Я обновил свой ответ. Я был немного неправ. В обоих случаях вы заметите, что заголовок появляется снова, когда вы нажимаете кнопку. Но когда вы отправляете сообщение самостоятельно, окно всегда анимируется при сворачивании. Если вы просто измените границу при нажатии кнопки, она не будет анимирована, когда вы сворачиваете ее через панель задач. - person Fayilt; 31.01.2014
comment
Извини, что вернулся так поздно, я думаю, этого достаточно. На самом деле это не так заметно, пока у вас не будет очень сложный контент с медленным рендерингом. - person pastillman; 05.02.2014
comment
В вашем первом взломе работает, но имеет неприятные последствия, приводящие к неправильному размеру окна при восстановлении окна. - person Scott Solmer; 07.07.2016
comment
Мне любопытно, почему никто не знает, как окна обрабатывают это обычно, поэтому мы можем изменить исходный код и изменить его для работы в нашем случае. - person The Muffin Man; 23.05.2020

Новая функция .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" в окне.

person bwing    schedule 02.05.2018
comment
Если для параметра AllowsTransparency установлено значение true и WindowStyle = none, анимация минимизации пропадет. - person The Muffin Man; 23.10.2020
comment
@TheMuffinMan Похоже, я столкнулся с той же проблемой, что и вы: мне нужно прозрачное окно и, тем не менее, сладкая анимация. Вы нашли другой способ? - person Angevil; 15.04.2021

Если вы обрабатываете сообщение WM_NCCALCSIZE, возвращая 0, обрабатываете сообщение WM_NCHITTEST, используя либо свой собственный код (если вы хотите выполнить проверку совпадения вручную), либо также возвращаете 0, и устанавливаете для WindowStyle значение SingleBorder, окно будет функционировать как окно без полей. но в нем будет включена анимация.

При полной необходимости вам также может потребоваться обработать WM_GETMINMAXINFO, чтобы исправить максимальный размер - он обрезает границы, потому что стиль окна - SingleBorder.

person rookie1024    schedule 09.03.2014

Я нашел другое решение, если вам нужно AllowTransparency = True. Это некрасиво, скорее немножко хакерское. Но это очень просто и отлично работает. При этом используется пустое окно, которое вскоре отображается при свертывании / развертывании / восстановлении окна, и оно имеет ту же позицию, ширину, размер и высоту, что и ваше окно. Он всегда имеет то же состояние окна, что и ваше окно, и выполняет анимацию, которой нет в YourWindow из-за WindowStyle None и AllowTransparency True. Пустое окно имеет стиль окна 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;
    }

Это удалит скрытое окно взлома, поэтому, если вы закроете настоящее окно, окно с хакерской анимацией тоже закроется. В противном случае он не будет виден пользователю, потому что он скрыт, но он все равно будет открыт, и поэтому части вашего приложения будут открыты. Это нежелательное поведение, поэтому укажите его как Закрытое событие:

    private void YourWindowClass_Closed(object sender, EventArgs e)
    {
        w.Close();
    }
person TheSkilluminati    schedule 15.07.2016
comment
Это совсем не работает. Главное окно отображается немного раньше, чем фальшивое окно завершает анимацию. - person The Muffin Man; 23.10.2020