diff --git a/MahApps.Metro/Controls/Flyout.cs b/MahApps.Metro/Controls/Flyout.cs
index e29893e892..05912f9c9f 100644
--- a/MahApps.Metro/Controls/Flyout.cs
+++ b/MahApps.Metro/Controls/Flyout.cs
@@ -1,7 +1,7 @@
using System;
-using System.Linq;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
@@ -15,6 +15,7 @@ namespace MahApps.Metro.Controls
///
///
[TemplatePart(Name = "PART_BackButton", Type = typeof(Button))]
+ [TemplatePart(Name = "PART_WindowTitleThumb", Type = typeof(Thumb))]
[TemplatePart(Name = "PART_Header", Type = typeof(ContentPresenter))]
[TemplatePart(Name = "PART_Content", Type = typeof(ContentPresenter))]
public class Flyout : ContentControl
@@ -212,6 +213,13 @@ public Flyout()
this.Loaded += (sender, args) => UpdateFlyoutTheme();
}
+ private MetroWindow parentWindow;
+
+ private MetroWindow ParentWindow
+ {
+ get { return this.parentWindow ?? (this.parentWindow = this.TryFindParent()); }
+ }
+
private void UpdateFlyoutTheme()
{
var flyoutsControl = this.TryFindParent();
@@ -221,7 +229,7 @@ private void UpdateFlyoutTheme()
this.Visibility = flyoutsControl != null ? Visibility.Collapsed : Visibility.Visible;
}
- var window = this.TryFindParent();
+ var window = this.ParentWindow;
if (window != null)
{
var windowTheme = DetectTheme(this);
@@ -282,7 +290,7 @@ private static Tuple DetectTheme(Flyout flyout)
return null;
// first look for owner
- var window = flyout.TryFindParent();
+ var window = flyout.ParentWindow;
var theme = window != null ? ThemeManager.DetectAppStyle(window) : null;
if (theme != null && theme.Item2 != null)
return theme;
@@ -460,6 +468,7 @@ static Flyout()
SplineDoubleKeyFrame fadeOutFrame;
ContentPresenter PART_Header;
ContentPresenter PART_Content;
+ Thumb windowTitleThumb;
public override void OnApplyTemplate()
{
@@ -471,7 +480,19 @@ public override void OnApplyTemplate()
PART_Header = (ContentPresenter)GetTemplateChild("PART_Header");
PART_Content = (ContentPresenter)GetTemplateChild("PART_Content");
-
+ this.windowTitleThumb = GetTemplateChild("PART_WindowTitleThumb") as Thumb;
+
+ if (this.windowTitleThumb != null)
+ {
+ this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
+ this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
+ this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
+
+ this.windowTitleThumb.DragDelta += this.WindowTitleThumbMoveOnDragDelta;
+ this.windowTitleThumb.MouseDoubleClick += this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
+ this.windowTitleThumb.MouseRightButtonUp += this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
+ }
+
hideStoryboard = (Storyboard)GetTemplateChild("HideStoryboard");
hideFrame = (SplineDoubleKeyFrame)GetTemplateChild("hideFrame");
hideFrameY = (SplineDoubleKeyFrame)GetTemplateChild("hideFrameY");
@@ -485,6 +506,44 @@ public override void OnApplyTemplate()
ApplyAnimation(Position, AnimateOpacity);
}
+ protected internal void CleanUp(FlyoutsControl flyoutsControl)
+ {
+ if (this.windowTitleThumb != null)
+ {
+ this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
+ this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
+ this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
+ }
+ this.parentWindow = null;
+ }
+
+ private void WindowTitleThumbMoveOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
+ {
+ var window = this.ParentWindow;
+ if (window != null && this.Position != Position.Bottom)
+ {
+ MetroWindow.DoWindowTitleThumbMoveOnDragDelta(window, dragDeltaEventArgs);
+ }
+ }
+
+ private void WindowTitleThumbChangeWindowStateOnMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
+ {
+ var window = this.ParentWindow;
+ if (window != null && this.Position != Position.Bottom)
+ {
+ MetroWindow.DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(window, mouseButtonEventArgs);
+ }
+ }
+
+ private void WindowTitleThumbSystemMenuOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ var window = this.ParentWindow;
+ if (window != null && this.Position != Position.Bottom)
+ {
+ MetroWindow.DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(window, e);
+ }
+ }
+
internal void ApplyAnimation(Position position, bool animateOpacity, bool resetShowFrame = true)
{
if (root == null || hideFrame == null || showFrame == null || hideFrameY == null || showFrameY == null || fadeOutFrame == null)
diff --git a/MahApps.Metro/Controls/FlyoutsControl.cs b/MahApps.Metro/Controls/FlyoutsControl.cs
index ad6c7486ec..66a2ae226c 100644
--- a/MahApps.Metro/Controls/FlyoutsControl.cs
+++ b/MahApps.Metro/Controls/FlyoutsControl.cs
@@ -58,6 +58,12 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
this.AttachHandlers((Flyout)element);
}
+ protected override void ClearContainerForItemOverride(DependencyObject element, object item)
+ {
+ ((Flyout) element).CleanUp(this);
+ base.ClearContainerForItemOverride(element, item);
+ }
+
private void AttachHandlers(Flyout flyout)
{
var isOpenNotifier = new PropertyChangeNotifier(flyout, Flyout.IsOpenProperty);
diff --git a/MahApps.Metro/Controls/MetroWindow.cs b/MahApps.Metro/Controls/MetroWindow.cs
index 82905574b9..bcd9a0436c 100644
--- a/MahApps.Metro/Controls/MetroWindow.cs
+++ b/MahApps.Metro/Controls/MetroWindow.cs
@@ -10,6 +10,7 @@
using MahApps.Metro.Native;
using System.Windows.Shapes;
using System.Collections.Generic;
+using System.Windows.Controls.Primitives;
namespace MahApps.Metro.Controls
{
@@ -19,6 +20,7 @@ namespace MahApps.Metro.Controls
[TemplatePart(Name = PART_Icon, Type = typeof(UIElement))]
[TemplatePart(Name = PART_TitleBar, Type = typeof(UIElement))]
[TemplatePart(Name = PART_WindowTitleBackground, Type = typeof(UIElement))]
+ [TemplatePart(Name = PART_WindowTitleThumb, Type = typeof(Thumb))]
[TemplatePart(Name = PART_LeftWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_RightWindowCommands, Type = typeof(WindowCommands))]
[TemplatePart(Name = PART_WindowButtonCommands, Type = typeof(WindowButtonCommands))]
@@ -31,6 +33,7 @@ public class MetroWindow : Window
private const string PART_Icon = "PART_Icon";
private const string PART_TitleBar = "PART_TitleBar";
private const string PART_WindowTitleBackground = "PART_WindowTitleBackground";
+ private const string PART_WindowTitleThumb = "PART_WindowTitleThumb";
private const string PART_LeftWindowCommands = "PART_LeftWindowCommands";
private const string PART_RightWindowCommands = "PART_RightWindowCommands";
private const string PART_WindowButtonCommands = "PART_WindowButtonCommands";
@@ -99,6 +102,7 @@ public class MetroWindow : Window
UIElement icon;
UIElement titleBar;
UIElement titleBarBackground;
+ Thumb windowTitleThumb;
internal ContentPresenter LeftWindowCommandsPresenter;
internal ContentPresenter RightWindowCommandsPresenter;
internal WindowButtonCommands WindowButtonCommands;
@@ -890,6 +894,7 @@ public override void OnApplyTemplate()
icon = GetTemplateChild(PART_Icon) as UIElement;
titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
titleBarBackground = GetTemplateChild(PART_WindowTitleBackground) as UIElement;
+ this.windowTitleThumb = GetTemplateChild(PART_WindowTitleThumb) as Thumb;
this.SetVisibiltyForAllTitleElements(this.TitlebarHeight > 0);
}
@@ -897,22 +902,16 @@ public override void OnApplyTemplate()
private void ClearWindowEvents()
{
// clear all event handlers first:
- if (titleBarBackground != null)
+ if (this.windowTitleThumb != null)
{
- titleBarBackground.MouseDown -= TitleBarMouseDown;
- titleBarBackground.MouseUp -= TitleBarMouseUp;
- }
- if (titleBar != null)
- {
- titleBar.MouseDown -= TitleBarMouseDown;
- titleBar.MouseUp -= TitleBarMouseUp;
+ this.windowTitleThumb.DragDelta -= this.WindowTitleThumbMoveOnDragDelta;
+ this.windowTitleThumb.MouseDoubleClick -= this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
+ this.windowTitleThumb.MouseRightButtonUp -= this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}
if (icon != null)
{
icon.MouseDown -= IconMouseDown;
}
- MouseDown -= TitleBarMouseDown;
- MouseUp -= TitleBarMouseUp;
SizeChanged -= MetroWindow_SizeChanged;
}
@@ -927,30 +926,17 @@ private void SetWindowEvents()
icon.MouseDown += IconMouseDown;
}
- // handle mouse events for PART_WindowTitleBackground -> MetroWindow
- if (titleBarBackground != null && titleBarBackground.Visibility == Visibility.Visible)
+ if (this.windowTitleThumb != null)
{
- titleBarBackground.MouseDown += TitleBarMouseDown;
- titleBarBackground.MouseUp += TitleBarMouseUp;
+ this.windowTitleThumb.DragDelta += this.WindowTitleThumbMoveOnDragDelta;
+ this.windowTitleThumb.MouseDoubleClick += this.WindowTitleThumbChangeWindowStateOnMouseDoubleClick;
+ this.windowTitleThumb.MouseRightButtonUp += this.WindowTitleThumbSystemMenuOnMouseRightButtonUp;
}
- // handle mouse events for PART_TitleBar -> MetroWindow
- if (titleBar != null && titleBar.Visibility == Visibility.Visible)
- {
- titleBar.MouseDown += TitleBarMouseDown;
- titleBar.MouseUp += TitleBarMouseUp;
-
- // special title resizing for centered title
- if (titleBar.GetType() == typeof(Grid))
- {
- SizeChanged += MetroWindow_SizeChanged;
- }
- }
- else
+ // handle size if we have a Grid for the title (e.g. clean window have a centered title)
+ if (titleBar != null && titleBar.GetType() == typeof(Grid))
{
- // handle mouse events for windows without PART_WindowTitleBackground or PART_TitleBar
- MouseDown += TitleBarMouseDown;
- MouseUp += TitleBarMouseUp;
+ SizeChanged += MetroWindow_SizeChanged;
}
}
@@ -969,49 +955,82 @@ private void IconMouseDown(object sender, MouseButtonEventArgs e)
}
}
- protected void TitleBarMouseDown(object sender, MouseButtonEventArgs e)
+ private void WindowTitleThumbMoveOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
+ {
+ DoWindowTitleThumbMoveOnDragDelta(this, dragDeltaEventArgs);
+ }
+
+ private void WindowTitleThumbChangeWindowStateOnMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
+ {
+ DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(this, mouseButtonEventArgs);
+ }
+
+ private void WindowTitleThumbSystemMenuOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
- // if UseNoneWindowStyle = true no movement, no maximize please
- if (e.ChangedButton == MouseButton.Left && !this.UseNoneWindowStyle)
+ DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(this, e);
+ }
+
+ internal static void DoWindowTitleThumbMoveOnDragDelta(MetroWindow window, DragDeltaEventArgs dragDeltaEventArgs)
+ {
+ // drag only if IsWindowDraggable is set to true
+ if (!window.IsWindowDraggable ||
+ (!(Math.Abs(dragDeltaEventArgs.HorizontalChange) > 2) &&
+ !(Math.Abs(dragDeltaEventArgs.VerticalChange) > 2))) return;
+
+ var windowHandle = new WindowInteropHelper(window).Handle;
+ var cursorPos = Standard.NativeMethods.GetCursorPos();
+
+ // if the window is maximized dragging is only allowed on title bar (also if not visible)
+ var windowIsMaximized = window.WindowState == WindowState.Maximized;
+ var isMouseOnTitlebar = cursorPos.y <= window.TitlebarHeight && window.TitlebarHeight > 0;
+ if (!isMouseOnTitlebar && windowIsMaximized)
{
- var mPoint = Mouse.GetPosition(this);
+ return;
+ }
- if (IsWindowDraggable)
- {
- IntPtr windowHandle = new WindowInteropHelper(this).Handle;
- UnsafeNativeMethods.ReleaseCapture();
- var wpfPoint = this.PointToScreen(mPoint);
- var x = Convert.ToInt16(wpfPoint.X);
- var y = Convert.ToInt16(wpfPoint.Y);
- var lParam = (int) (uint) x | (y << 16);
- UnsafeNativeMethods.SendMessage(windowHandle, Constants.WM_NCLBUTTONDOWN, Constants.HT_CAPTION, lParam);
- }
+ if (windowIsMaximized)
+ {
+ window.Top = 2;
+ window.Left = Math.Max(cursorPos.x - window.RestoreBounds.Width / 2, 0);
+ }
+ var lParam = (int)(uint)cursorPos.x | (cursorPos.y << 16);
+ Standard.NativeMethods.SendMessage(windowHandle, Standard.WM.LBUTTONUP, (IntPtr)Standard.HT.CAPTION, (IntPtr)lParam);
+ Standard.NativeMethods.SendMessage(windowHandle, Standard.WM.SYSCOMMAND, (IntPtr)Standard.SC.MOUSEMOVE, IntPtr.Zero);
+ }
- var canResize = this.ResizeMode == ResizeMode.CanResizeWithGrip || this.ResizeMode == ResizeMode.CanResize;
+ internal static void DoWindowTitleThumbChangeWindowStateOnMouseDoubleClick(MetroWindow window, MouseButtonEventArgs mouseButtonEventArgs)
+ {
+ // restore/maximize only with left button
+ if (mouseButtonEventArgs.ChangedButton == MouseButton.Left)
+ {
// we can maximize or restore the window if the title bar height is set (also if title bar is hidden)
- var isMouseOnTitlebar = mPoint.Y <= this.TitlebarHeight && this.TitlebarHeight > 0;
- if (e.ClickCount == 2 && canResize && isMouseOnTitlebar)
+ var canResize = window.ResizeMode == ResizeMode.CanResizeWithGrip || window.ResizeMode == ResizeMode.CanResize;
+ var mousePos = Mouse.GetPosition(window);
+ var isMouseOnTitlebar = mousePos.Y <= window.TitlebarHeight && window.TitlebarHeight > 0;
+ if (canResize && isMouseOnTitlebar)
{
- if (this.WindowState == WindowState.Maximized)
+ if (window.WindowState == WindowState.Maximized)
{
- Microsoft.Windows.Shell.SystemCommands.RestoreWindow(this);
+ Microsoft.Windows.Shell.SystemCommands.RestoreWindow(window);
}
else
{
- Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(this);
+ Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(window);
}
+ mouseButtonEventArgs.Handled = true;
}
}
}
- protected void TitleBarMouseUp(object sender, MouseButtonEventArgs e)
+ internal static void DoWindowTitleThumbSystemMenuOnMouseRightButtonUp(MetroWindow window, MouseButtonEventArgs e)
{
- if (ShowSystemMenuOnRightClick)
+ if (window.ShowSystemMenuOnRightClick)
{
- var mousePosition = e.GetPosition(this);
- if (e.ChangedButton == MouseButton.Right && (UseNoneWindowStyle || mousePosition.Y <= TitlebarHeight))
+ // show menu only if mouse pos is on title bar or if we have a window with none style and no title bar
+ var mousePos = e.GetPosition(window);
+ if ((mousePos.Y <= window.TitlebarHeight && window.TitlebarHeight > 0) || (window.UseNoneWindowStyle && window.TitlebarHeight <= 0))
{
- ShowSystemMenuPhysicalCoordinates(this, PointToScreen(mousePosition));
+ ShowSystemMenuPhysicalCoordinates(window, window.PointToScreen(mousePos));
}
}
}
diff --git a/MahApps.Metro/MahApps.Metro.NET45.csproj b/MahApps.Metro/MahApps.Metro.NET45.csproj
index cd3a328e33..184de5b9b1 100644
--- a/MahApps.Metro/MahApps.Metro.NET45.csproj
+++ b/MahApps.Metro/MahApps.Metro.NET45.csproj
@@ -642,6 +642,10 @@
MSBuild:Compile
Designer
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/MahApps.Metro/MahApps.Metro.csproj b/MahApps.Metro/MahApps.Metro.csproj
index 9ee1adad54..7b281af575 100644
--- a/MahApps.Metro/MahApps.Metro.csproj
+++ b/MahApps.Metro/MahApps.Metro.csproj
@@ -593,6 +593,10 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
diff --git a/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs b/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs
index c2a0a29c51..27f920948b 100644
--- a/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs
+++ b/MahApps.Metro/Microsoft.Windows.Shell/Standard/NativeMethods.cs
@@ -1160,6 +1160,7 @@ internal enum SC
{
SIZE = 0xF000,
MOVE = 0xF010,
+ MOUSEMOVE = 0xF012,
MINIMIZE = 0xF020,
MAXIMIZE = 0xF030,
NEXTWINDOW = 0xF040,
diff --git a/MahApps.Metro/Themes/Flyout.xaml b/MahApps.Metro/Themes/Flyout.xaml
index 78208adc23..c8d2879f3e 100644
--- a/MahApps.Metro/Themes/Flyout.xaml
+++ b/MahApps.Metro/Themes/Flyout.xaml
@@ -7,6 +7,7 @@
+
+
+
@@ -92,6 +93,12 @@
+
+
+
+
+
@@ -364,6 +374,12 @@
+
+
+
+
+
diff --git a/MahApps.Metro/Themes/Thumb.xaml b/MahApps.Metro/Themes/Thumb.xaml
new file mode 100644
index 0000000000..ec2c600abe
--- /dev/null
+++ b/MahApps.Metro/Themes/Thumb.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
\ No newline at end of file