2014-01-28 4 views
3

Я хотел иметь настраиваемое окно, а затем несколько учебных пособий, которые позволяют это, установив стиль окна в none, а затем самостоятельно добавив кнопки заголовка/восстановления/минимизации/закрытия. Сведение к минимуму достигается за счет простого обращения к событию кликов и сглаживания состояния окна, но это не показывает анимацию минимизации, которую вы видите в Windows 7, и просто скрывает окно, которое кажется очень странным при использовании с другими окнами которые ожидают, поскольку вы склонны чувствовать, что приложение закрывается.Пользовательский стиль окна с минимизацией анимации

Итак, есть ли способ разрешить эту анимацию? .. он кажется отключенным, когда вы меняете WindowStyle на none.

Edit: Код испытания

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

ответ

6

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

Есть два варианта: 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; 
} 

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

+0

Thankyou. Я смог получить второй подход к работе, но не первый. Первый пока не показывает анимацию. Я отредактировал мой пост с кодом Im using – pastillman

+0

Я обновил свой ответ. Я был немного не прав. В обоих случаях вы заметите, что заголовок снова появляется, когда вы нажимаете кнопку. Но когда вы отправляете сообщение самостоятельно, окно всегда анимируется при минимизации. Если вы просто изменяете границу при нажатии кнопки, она не анимируется, когда вы сворачиваете ее с помощью панели задач. – Fayilt

+0

Извините за то, что так поздно, так что думаю. На самом деле это не так заметно, пока у вас не будет очень сложного контента с лагиным рендерингом. – pastillman

1

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

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

0

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

Наконец, создать эту ASync задачу в YourWindowClass. Он скоро подождёт, а затем скрывает дополнительное окно.

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

Это удалит скрытое окно хак, так что если вы закроете реальное окно, Hacky анимации Окно закроется тоже. Иначе это не было бы видимым для пользователя, потому что оно скрыто, но оно все равно будет открытым, поэтому части вашего приложения открыты. Это поведение, которое мы не хотим, поэтому поставьте это как ваше закрытое мероприятие:

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