diff --git a/MahApps.Metro/Behaviours/BorderlessWindowBehavior.cs b/MahApps.Metro/Behaviours/BorderlessWindowBehavior.cs index 86cf7fcf4b..6463fffcfa 100644 --- a/MahApps.Metro/Behaviours/BorderlessWindowBehavior.cs +++ b/MahApps.Metro/Behaviours/BorderlessWindowBehavior.cs @@ -35,6 +35,8 @@ protected override void OnAttached() windowChrome.IgnoreTaskbarOnMaximize = metroWindow.IgnoreTaskbarOnMaximize; System.ComponentModel.DependencyPropertyDescriptor.FromProperty(MetroWindow.IgnoreTaskbarOnMaximizeProperty, typeof(MetroWindow)) .AddValueChanged(AssociatedObject, IgnoreTaskbarOnMaximizePropertyChangedCallback); + System.ComponentModel.DependencyPropertyDescriptor.FromProperty(MetroWindow.UseNoneWindowStyleProperty, typeof(MetroWindow)) + .AddValueChanged(AssociatedObject, UseNoneWindowStylePropertyChangedCallback); } AssociatedObject.SetValue(WindowChrome.WindowChromeProperty, windowChrome); @@ -52,7 +54,6 @@ protected override void OnAttached() //For some reason, we can't determine if the window has loaded or not, so we swallow the exception. } } - AssociatedObject.WindowStyle = WindowStyle.None; savedBorderThickness = AssociatedObject.BorderThickness; AssociatedObject.Loaded += AssociatedObject_Loaded; @@ -70,6 +71,17 @@ private void IgnoreTaskbarOnMaximizePropertyChangedCallback(object sender, Event if (metroWindow != null && windowChrome != null) { windowChrome.IgnoreTaskbarOnMaximize = metroWindow.IgnoreTaskbarOnMaximize; + UpdateWindowStyle(); + } + } + + private void UseNoneWindowStylePropertyChangedCallback(object sender, EventArgs e) + { + var metroWindow = sender as MetroWindow; + if(metroWindow != null && windowChrome != null) + { + windowChrome.UseNoneWindowStyle = metroWindow.UseNoneWindowStyle; + UpdateWindowStyle(); } } @@ -86,6 +98,8 @@ private void Cleanup() { System.ComponentModel.DependencyPropertyDescriptor.FromProperty(MetroWindow.IgnoreTaskbarOnMaximizeProperty, typeof(MetroWindow)) .RemoveValueChanged(AssociatedObject, IgnoreTaskbarOnMaximizePropertyChangedCallback); + System.ComponentModel.DependencyPropertyDescriptor.FromProperty(MetroWindow.UseNoneWindowStyleProperty, typeof(MetroWindow)) + .RemoveValueChanged(AssociatedObject, UseNoneWindowStylePropertyChangedCallback); } AssociatedObject.Loaded -= AssociatedObject_Loaded; AssociatedObject.Unloaded -= AssociatedObject_Unloaded; @@ -163,7 +177,8 @@ private void AssociatedObject_StateChanged(object sender, EventArgs e) private void HandleMaximize(bool handleOnlyMaximized = false) { - if (AssociatedObject.WindowState == WindowState.Maximized) + var metroWindow = AssociatedObject as MetroWindow; + if (AssociatedObject.WindowState == WindowState.Maximized && metroWindow.IgnoreTaskbarOnMaximize) { // remove resize border and window border, so we can move the window from top monitor position windowChrome.ResizeBorderThickness = new Thickness(0); @@ -175,7 +190,6 @@ private void HandleMaximize(bool handleOnlyMaximized = false) if (monitor != IntPtr.Zero) { var monitorInfo = new MONITORINFO(); UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); - var metroWindow = AssociatedObject as MetroWindow; var ignoreTaskBar = metroWindow != null && (metroWindow.IgnoreTaskbarOnMaximize || metroWindow.UseNoneWindowStyle); var x = ignoreTaskBar ? monitorInfo.rcMonitor.left : monitorInfo.rcWork.left; var y = ignoreTaskBar ? monitorInfo.rcMonitor.top : monitorInfo.rcWork.top; @@ -201,6 +215,15 @@ private void HandleMaximize(bool handleOnlyMaximized = false) } } + private void UpdateWindowStyle() + { + var metroWindow = AssociatedObject as MetroWindow; + if(metroWindow != null) + { + metroWindow.WindowStyle = (metroWindow.IgnoreTaskbarOnMaximize || metroWindow.UseNoneWindowStyle) ? WindowStyle.None : WindowStyle.SingleBorderWindow; + } + } + private void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam) { MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO)); @@ -318,6 +341,7 @@ private void AssociatedObject_SourceInitialized(object sender, EventArgs e) AssociatedObject.SizeToContent = sizeToContent == SizeToContent.WidthAndHeight ? SizeToContent.Height : SizeToContent.Manual; AssociatedObject.SizeToContent = sizeToContent; AssociatedObject.SnapsToDevicePixels = snapsToDevicePixels; + UpdateWindowStyle(); } private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) diff --git a/MahApps.Metro/Behaviours/GlowWindowBehavior.cs b/MahApps.Metro/Behaviours/GlowWindowBehavior.cs index 81ed91e697..fb054c7d77 100644 --- a/MahApps.Metro/Behaviours/GlowWindowBehavior.cs +++ b/MahApps.Metro/Behaviours/GlowWindowBehavior.cs @@ -2,22 +2,93 @@ using System.Windows; using System.Windows.Interactivity; using MahApps.Metro.Controls; +using System.Windows.Threading; namespace MahApps.Metro.Behaviours { public class GlowWindowBehavior : Behavior { + private const int glowTimerDelay = 200; //200 ms delay, the same as VS2013 private GlowWindow left, right, top, bottom; - + private DispatcherTimer makeGlowVisibleTimer; + protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.Loaded += AssociatedObjectOnLoaded; + this.AssociatedObject.Unloaded += AssociatedObjectUnloaded; + this.AssociatedObject.StateChanged += AssociatedObjectStateChanged; + } + + void AssociatedObjectStateChanged(object sender, EventArgs e) + { + makeGlowVisibleTimer.Stop(); + if(AssociatedObject.WindowState != WindowState.Minimized) + { + if(AssociatedObject.WindowStyle == WindowStyle.None || !SystemParameters.MinimizeAnimation) + { + RestoreGlow(); + } + else + { + makeGlowVisibleTimer.Start(); + } + } + else + { + HideGlow(); + } + } + + void AssociatedObjectUnloaded(object sender, RoutedEventArgs e) + { + if(makeGlowVisibleTimer != null) + { + makeGlowVisibleTimer.Stop(); + makeGlowVisibleTimer.Tick -= makeGlowVisibleTimer_Tick; + makeGlowVisibleTimer = null; + } + } + + private void makeGlowVisibleTimer_Tick(object sender, EventArgs e) + { + if(makeGlowVisibleTimer != null) + { + makeGlowVisibleTimer.Stop(); + } + RestoreGlow(); + } + + private void RestoreGlow() + { + if(left != null && top != null && right != null && bottom != null) + { + left.IsGlowing = top.IsGlowing = right.IsGlowing = bottom.IsGlowing = true; + Update(); + } + } + + private void HideGlow() + { + if (left != null && top != null && right != null && bottom != null) + { + left.IsGlowing = top.IsGlowing = right.IsGlowing = bottom.IsGlowing = false; + Update(); + } } private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs) { + if(makeGlowVisibleTimer == null) + { + makeGlowVisibleTimer = new DispatcherTimer() + { + Interval = TimeSpan.FromMilliseconds(glowTimerDelay) + }; + makeGlowVisibleTimer.Tick += makeGlowVisibleTimer_Tick; + } + // now glow effect if UseNoneWindowStyle is true or GlowBrush not set var metroWindow = this.AssociatedObject as MetroWindow; if (metroWindow != null && (metroWindow.UseNoneWindowStyle || metroWindow.GlowBrush == null)) diff --git a/MahApps.Metro/Controls/GlowWindow.xaml.cs b/MahApps.Metro/Controls/GlowWindow.xaml.cs index 96675da046..46da0ac3dd 100644 --- a/MahApps.Metro/Controls/GlowWindow.xaml.cs +++ b/MahApps.Metro/Controls/GlowWindow.xaml.cs @@ -36,6 +36,7 @@ public GlowWindow(Window owner, GlowDirection direction) // windowChrome.GlassFrameThickness = new Thickness(-1); // windowChrome.UseAeroCaptionButtons = false; // this.SetValue(WindowChrome.WindowChromeProperty, windowChrome); + this.IsGlowing = true; this.AllowsTransparency = true; this.Owner = owner; @@ -232,7 +233,7 @@ public void Update() { if (this.closing) return; - Visibility = Visibility.Visible; + Visibility = IsGlowing ? Visibility.Visible : Visibility.Collapsed; UpdateCore(); } @@ -242,6 +243,12 @@ public void Update() } } + public bool IsGlowing + { + set; + get; + } + private void UpdateCore() { if (ownerHandle == IntPtr.Zero) @@ -261,6 +268,13 @@ private void UpdateCore() private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { + if (msg == (int)WM.SHOWWINDOW) + { + if((int)lParam == 3 && this.Visibility != Visibility.Visible) // 3 == SW_PARENTOPENING + { + handled = true; //handle this message so window isn't shown until we want it to + } + } if (msg == (int)WM.MOUSEACTIVATE) { handled = true; diff --git a/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs b/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs index dd6bd2db82..a5d88017a5 100644 --- a/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs +++ b/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs @@ -2323,6 +2323,21 @@ internal struct WNDCLASSEX public IntPtr hIconSm; } + [StructLayout(LayoutKind.Sequential)] + internal struct WINDOWINFO + { + public int cbSize; + public RECT rcWindow; + public RECT rcClient; + public int dwStyle; + public int dwExStyle; + public uint dwWindowStatus; + public uint cxWindowBorders; + public uint cyWindowBorders; + public ushort atomWindowType; + public ushort wCreatorVersion; + } + [StructLayout(LayoutKind.Sequential)] internal struct MOUSEINPUT { @@ -2567,6 +2582,10 @@ public static HRESULT ChangeWindowMessageFilterEx(IntPtr hwnd, WM message, MSGFL return HRESULT.S_OK; } + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [DllImport("user32.dll", CharSet = CharSet.None, SetLastError = true, EntryPoint = "ClientToScreen")] + public static extern bool ClientToScreen(IntPtr hWnd, ref POINT point); + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [DllImport("gdi32.dll")] public static extern CombineRgnResult CombineRgn(IntPtr hrgnDest, IntPtr hrgnSrc1, IntPtr hrgnSrc2, RGN fnCombineMode); @@ -3063,6 +3082,23 @@ public static IntPtr GetStockObject(StockObject fnObject) [DllImport("user32.dll")] public static extern int GetSystemMetrics(SM nIndex); + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [DllImport("user32.dll", CharSet = CharSet.None, SetLastError = true, EntryPoint = "GetWindowInfo")] + private static extern bool _GetWindowInfo(IntPtr hWnd, ref WINDOWINFO pwi); + + public static WINDOWINFO GetWindowInfo(IntPtr hWnd) + { + WINDOWINFO info = new WINDOWINFO() + { + cbSize = Marshal.SizeOf(typeof(WINDOWINFO)) + }; + if (!_GetWindowInfo(hWnd, ref info)) + { + HRESULT.ThrowLastError(); + } + return info; + } + // This is aliased as a macro in 32bit Windows. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static IntPtr GetWindowLongPtr(IntPtr hwnd, GWL nIndex) diff --git a/MahApps.Metro/Microsoft.Windows.Shell/WindowChrome.cs b/MahApps.Metro/Microsoft.Windows.Shell/WindowChrome.cs index b0f73f306b..3a6ef713c5 100644 --- a/MahApps.Metro/Microsoft.Windows.Shell/WindowChrome.cs +++ b/MahApps.Metro/Microsoft.Windows.Shell/WindowChrome.cs @@ -254,6 +254,17 @@ public bool IgnoreTaskbarOnMaximize set { SetValue(IgnoreTaskbarOnMaximizeProperty, value); } } + public static readonly DependencyProperty UseNoneWindowStyleProperty = DependencyProperty.Register( + "UseNoneWindowStyle", + typeof(bool), + typeof(WindowChrome), + new FrameworkPropertyMetadata(false)); + public bool UseNoneWindowStyle + { + get { return (bool)GetValue(UseNoneWindowStyleProperty); } + set { SetValue(UseNoneWindowStyleProperty, value); } + } + public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register( "CornerRadius", typeof(CornerRadius), diff --git a/MahApps.Metro/Microsoft.Windows.Shell/WindowChromeWorker.cs b/MahApps.Metro/Microsoft.Windows.Shell/WindowChromeWorker.cs index bd1853cb3f..553c843a94 100644 --- a/MahApps.Metro/Microsoft.Windows.Shell/WindowChromeWorker.cs +++ b/MahApps.Metro/Microsoft.Windows.Shell/WindowChromeWorker.cs @@ -486,10 +486,16 @@ private IntPtr _HandleNCCalcSize(WM uMsg, IntPtr wParam, IntPtr lParam, out bool // Since the first field of NCCALCSIZE_PARAMS is a RECT and is the only field we care about // we can unconditionally treat it as a RECT. - if ((int)wParam == 1) + if (NativeMethods.GetWindowPlacement(_hwnd).showCmd == SW.MAXIMIZE) { - handled = true; - return IntPtr.Zero; + if(SystemParameters.MinimizeAnimation && _chromeInfo.IgnoreTaskbarOnMaximize == false && _chromeInfo.UseNoneWindowStyle == false) + { + RECT rc = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); + NativeMethods.DefWindowProc(_hwnd, WM.NCCALCSIZE, wParam, lParam); + RECT def = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); + def.Top = (int)(rc.Top + NativeMethods.GetWindowInfo(_hwnd).cyWindowBorders); + Marshal.StructureToPtr(def, lParam, true); + } } if (_chromeInfo.SacrificialEdge != SacrificialEdge.None) @@ -514,10 +520,13 @@ private IntPtr _HandleNCCalcSize(WM uMsg, IntPtr wParam, IntPtr lParam, out bool } Marshal.StructureToPtr(rcClientArea, lParam, false); + + handled = true; + return new IntPtr((int)WVR.REDRAW); } handled = true; - return new IntPtr((int)WVR.REDRAW); + return IntPtr.Zero; } private HT _GetHTFromResizeGripDirection(ResizeGripDirection direction) @@ -888,38 +897,61 @@ private void _ClearRoundingRegion() NativeMethods.SetWindowRgn(_hwnd, IntPtr.Zero, NativeMethods.IsWindowVisible(_hwnd)); } + private static RECT _GetClientRectRelativeToWindowRect(IntPtr hWnd) + { + RECT windowRect = NativeMethods.GetWindowRect(hWnd); + RECT clientRect = NativeMethods.GetClientRect(hWnd); + + POINT origin = new POINT() + { + x = 0, + y = 0 + }; + NativeMethods.ClientToScreen(hWnd, ref origin); + clientRect.Offset(origin.x - windowRect.Left, origin.y - windowRect.Top); + return clientRect; + } + private void _SetRoundingRegion(WINDOWPOS? wp) { - const int MONITOR_DEFAULTTONEAREST = 0x00000002; // We're early - WPF hasn't necessarily updated the state of the window. // Need to query it ourselves. WINDOWPLACEMENT wpl = NativeMethods.GetWindowPlacement(_hwnd); + const int MONITOR_DEFAULTTONEAREST = 0x00000002; if (wpl.showCmd == SW.SHOWMAXIMIZED) { - int left; - int top; - - if (wp.HasValue) + RECT rcMax; + if (_chromeInfo.UseNoneWindowStyle == false && _chromeInfo.IgnoreTaskbarOnMaximize == false && SystemParameters.MinimizeAnimation) { - left = wp.Value.x; - top = wp.Value.y; + rcMax = _GetClientRectRelativeToWindowRect(_hwnd); } else { - Rect r = _GetWindowRect(); - left = (int)r.Left; - top = (int)r.Top; - } + int left; + int top; + + if (wp.HasValue) + { + left = wp.Value.x; + top = wp.Value.y; + } + else + { + Rect r = _GetWindowRect(); + left = (int)r.Left; + top = (int)r.Top; + } - IntPtr hMon = NativeMethods.MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); + IntPtr hMon = NativeMethods.MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi = NativeMethods.GetMonitorInfo(hMon); - RECT rcMax = _chromeInfo.IgnoreTaskbarOnMaximize ? mi.rcMonitor : mi.rcWork; - // The location of maximized window takes into account the border that Windows was - // going to remove, so we also need to consider it. - rcMax.Offset(-left, -top); + MONITORINFO mi = NativeMethods.GetMonitorInfo(hMon); + rcMax = _chromeInfo.IgnoreTaskbarOnMaximize ? mi.rcMonitor : mi.rcWork; + // The location of maximized window takes into account the border that Windows was + // going to remove, so we also need to consider it. + rcMax.Offset(-left, -top); + } IntPtr hrgn = IntPtr.Zero; try @@ -981,7 +1013,7 @@ private void _SetRoundingRegion(WINDOWPOS? wp) Assert.AreEqual(topRightRegionRect.Right, windowSize.Width); _CreateAndCombineRoundRectRgn(hrgn, topRightRegionRect, topRightRadius); - + double bottomLeftRadius = DpiHelper.LogicalPixelsToDevice(new Point(_chromeInfo.CornerRadius.BottomLeft, 0)).X; bottomLeftRadius = Math.Min(bottomLeftRadius, shortestDimension / 2); Rect bottomLeftRegionRect = new Rect(0, 0, windowSize.Width / 2 + bottomLeftRadius, windowSize.Height / 2 + bottomLeftRadius); @@ -989,7 +1021,7 @@ private void _SetRoundingRegion(WINDOWPOS? wp) Assert.AreEqual(bottomLeftRegionRect.Bottom, windowSize.Height); _CreateAndCombineRoundRectRgn(hrgn, bottomLeftRegionRect, bottomLeftRadius); - + double bottomRightRadius = DpiHelper.LogicalPixelsToDevice(new Point(_chromeInfo.CornerRadius.BottomRight, 0)).X; bottomRightRadius = Math.Min(bottomRightRadius, shortestDimension / 2); Rect bottomRightRegionRect = new Rect(0, 0, windowSize.Width / 2 + bottomRightRadius, windowSize.Height / 2 + bottomRightRadius); diff --git a/samples/MetroDemo/MainWindow.xaml b/samples/MetroDemo/MainWindow.xaml index 26b24d169f..b732d68dc0 100644 --- a/samples/MetroDemo/MainWindow.xaml +++ b/samples/MetroDemo/MainWindow.xaml @@ -157,6 +157,9 @@ + + + diff --git a/samples/MetroDemo/MainWindow.xaml.cs b/samples/MetroDemo/MainWindow.xaml.cs index 0d449e4c75..047777b1ac 100644 --- a/samples/MetroDemo/MainWindow.xaml.cs +++ b/samples/MetroDemo/MainWindow.xaml.cs @@ -224,5 +224,10 @@ private async void MetroWindow_Closing(object sender, System.ComponentModel.Canc if (_shutdown) Application.Current.Shutdown(); } + + private void IgnoreTaskBar_Click(object sender, RoutedEventArgs e) + { + this.IgnoreTaskbarOnMaximize = !this.IgnoreTaskbarOnMaximize; + } } }