diff --git a/BrightSharp.Ui.Tests/CustomWindow.xaml b/BrightSharp.Ui.Tests/CustomWindow.xaml index 2932427..a9b85f6 100644 --- a/BrightSharp.Ui.Tests/CustomWindow.xaml +++ b/BrightSharp.Ui.Tests/CustomWindow.xaml @@ -4,17 +4,17 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:bs="http://schemas.brightsharp.com/developer" - xmlns:conv="http://schemas.brightsharp.com/developer" xmlns:local="clr-namespace:BrightSharp.Ui.Tests" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" mc:Ignorable="d" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow" Title="Brushes Explorer" + bs:MarkupExtensionProperties.UseMinMaxSizeBehavior="True" Height="800" Width="600" Style="{DynamicResource BrightSharpWindowStyle}"> - + diff --git a/BrightSharp.Ui.Tests/MainWindow.xaml b/BrightSharp.Ui.Tests/MainWindow.xaml index c665343..96e75ea 100644 --- a/BrightSharp.Ui.Tests/MainWindow.xaml +++ b/BrightSharp.Ui.Tests/MainWindow.xaml @@ -5,6 +5,7 @@ xmlns:viewModel="clr-namespace:BrightSharp.Ui.Tests" xmlns:ex="http://schemas.brightsharp.com/developer" xmlns:bsDiag="http://schemas.brightsharp.com/diagrams" + ex:MarkupExtensionProperties.UseMinMaxSizeBehavior="True" Style="{DynamicResource BrightSharpWindowStyle}" ResizeMode="CanResizeWithGrip" x:Class="BrightSharp.Ui.Tests.MainWindow" diff --git a/BrightSharp/Behaviors/WindowMinMaxSizeBehavior.cs b/BrightSharp/Behaviors/WindowMinMaxSizeBehavior.cs new file mode 100644 index 0000000..3ff9e4d --- /dev/null +++ b/BrightSharp/Behaviors/WindowMinMaxSizeBehavior.cs @@ -0,0 +1,78 @@ +using BrightSharp.Interop; +using BrightSharp.Interop.Constants; +using BrightSharp.Interop.Structures; +using System; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interactivity; +using System.Windows.Interop; + +namespace BrightSharp.Behaviors +{ + public class WindowMinMaxSizeBehavior : Behavior + { + private HwndSource hwndSource; + + protected override void OnAttached() + { + base.OnAttached(); + + AssociatedObject.SourceInitialized += AssociatedObject_SourceInitialized; + } + + private void AssociatedObject_SourceInitialized(object sender, EventArgs e) + { + IntPtr handle = (new WindowInteropHelper(AssociatedObject)).Handle; + hwndSource = HwndSource.FromHwnd(handle); + hwndSource.AddHook(WindowProc); + } + + protected override void OnDetaching() + { + AssociatedObject.SourceInitialized -= AssociatedObject_SourceInitialized; + if (hwndSource != null) + { + hwndSource.RemoveHook(WindowProc); + hwndSource.Dispose(); + } + base.OnDetaching(); + } + + private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + switch (msg) + { + case 0x0024: + WmGetMinMaxInfo(hwnd, lParam); + handled = true; + break; + } + return (IntPtr)0; + } + + private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam) + { + var mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO)); + + // Adjust the maximized size and position to fit the work area of the correct monitor + IntPtr monitor = NativeMethods.MonitorFromWindow(hwnd, (int)MonitorFromWindowFlags.MONITOR_DEFAULTTONEAREST); + + if (monitor != IntPtr.Zero) + { + var monitorInfo = new MONITORINFO(); + NativeMethods.GetMonitorInfo(monitor, monitorInfo); + RECT rcWorkArea = monitorInfo.rcWork; + RECT rcMonitorArea = monitorInfo.rcMonitor; + mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.Left - rcMonitorArea.Left); + mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.Top - rcMonitorArea.Top); + mmi.ptMaxSize.x = Math.Abs(rcWorkArea.Right - rcWorkArea.Left); + mmi.ptMaxSize.y = Math.Abs(rcWorkArea.Bottom - rcWorkArea.Top); + mmi.ptMinTrackSize.x = (int)AssociatedObject.MinWidth; + mmi.ptMinTrackSize.y = (int)AssociatedObject.MinHeight; + } + + Marshal.StructureToPtr(mmi, lParam, true); + } + + } +} diff --git a/BrightSharp/BrightSharp.csproj b/BrightSharp/BrightSharp.csproj index 5a42609..7ad816c 100644 --- a/BrightSharp/BrightSharp.csproj +++ b/BrightSharp/BrightSharp.csproj @@ -53,6 +53,7 @@ + @@ -68,6 +69,9 @@ + + + Theme.xaml diff --git a/BrightSharp/Extensions/MarkupExtensionProperties.cs b/BrightSharp/Extensions/MarkupExtensionProperties.cs index 984bd30..4aed7ee 100644 --- a/BrightSharp/Extensions/MarkupExtensionProperties.cs +++ b/BrightSharp/Extensions/MarkupExtensionProperties.cs @@ -1,5 +1,9 @@ -using System.Windows; +using BrightSharp.Behaviors; +using System; +using System.Linq; +using System.Windows; using System.Windows.Controls; +using System.Windows.Interactivity; using System.Windows.Media; namespace BrightSharp.Extensions @@ -120,6 +124,16 @@ namespace BrightSharp.Extensions obj.SetValue(SpecialBrushProperty, value); } + public static bool GetUseMinMaxSizeBehavior(Window obj) + { + return (bool)obj.GetValue(UseMinMaxSizeBehaviorProperty); + } + + public static void SetUseMinMaxSizeBehavior(Window obj, bool value) + { + obj.SetValue(UseMinMaxSizeBehaviorProperty, value); + } + public static readonly DependencyProperty SpecialBrushProperty = DependencyProperty.RegisterAttached("SpecialBrush", typeof(Brush), typeof(MarkupExtensionProperties), new PropertyMetadata(null)); public static readonly DependencyProperty IsDragHelperVisibleProperty = DependencyProperty.RegisterAttached("IsDragHelperVisible", typeof(bool), typeof(MarkupExtensionProperties), new FrameworkPropertyMetadata(true)); public static readonly DependencyProperty HeaderPaddingProperty = DependencyProperty.RegisterAttached("HeaderPadding", typeof(Thickness), typeof(MarkupExtensionProperties), new PropertyMetadata(null)); @@ -134,7 +148,23 @@ namespace BrightSharp.Extensions public static readonly DependencyProperty HeaderHorizontalAlignmentProperty = DependencyProperty.RegisterAttached("HeaderHorizontalAlignment", typeof(HorizontalAlignment), typeof(MarkupExtensionProperties), new PropertyMetadata(HorizontalAlignment.Left)); public static readonly DependencyProperty SpecialHeightProperty = DependencyProperty.RegisterAttached("SpecialHeight", typeof(double), typeof(MarkupExtensionProperties), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); public static readonly DependencyProperty SpecialWidthProperty = DependencyProperty.RegisterAttached("SpecialWidth", typeof(double), typeof(MarkupExtensionProperties), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); - + public static readonly DependencyProperty UseMinMaxSizeBehaviorProperty = DependencyProperty.RegisterAttached("UseMinMaxSizeBehavior", typeof(bool), typeof(MarkupExtensionProperties), new PropertyMetadata(false, UseMinMaxSizeBehaviorChanged)); + + + private static void UseMinMaxSizeBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = d as Window; + if (window == null) return; + if ((bool)e.NewValue) + { + Interaction.GetBehaviors(window).Add(new WindowMinMaxSizeBehavior()); + } + else + { + var beh = Interaction.GetBehaviors(window).OfType().FirstOrDefault(); + if (beh != null) Interaction.GetBehaviors(window).Remove(beh); + } + } } } diff --git a/BrightSharp/Interop/Constants.cs b/BrightSharp/Interop/Constants.cs new file mode 100644 index 0000000..d18d5fe --- /dev/null +++ b/BrightSharp/Interop/Constants.cs @@ -0,0 +1,11 @@ +namespace BrightSharp.Interop +{ + namespace Constants + { + internal enum MonitorFromWindowFlags + { + MONITOR_DEFAULTTONEAREST = 0x00000002 + } + + } +} diff --git a/BrightSharp/Interop/NativeMethods.cs b/BrightSharp/Interop/NativeMethods.cs new file mode 100644 index 0000000..2c75530 --- /dev/null +++ b/BrightSharp/Interop/NativeMethods.cs @@ -0,0 +1,20 @@ +using BrightSharp.Interop.Structures; +using System; +using System.Runtime.InteropServices; + +namespace BrightSharp.Interop +{ + internal static class NativeMethods + { + #region Monitor + + [DllImport("user32.dll", SetLastError = true)] + internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags); + + #endregion + + } +} diff --git a/BrightSharp/Interop/Structures.cs b/BrightSharp/Interop/Structures.cs new file mode 100644 index 0000000..16a6ef2 --- /dev/null +++ b/BrightSharp/Interop/Structures.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.InteropServices; + +namespace BrightSharp.Interop +{ + namespace Structures + { + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal class MONITORINFO + { + public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); + + public RECT rcMonitor; + + public RECT rcWork; + + public int dwFlags; + } + + [StructLayout(LayoutKind.Sequential, Pack = 0)] + internal struct POINT + { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential, Pack = 0)] + internal struct RECT + { + public int Left; + + public int Top; + + public int Right; + + public int Bottom; + + public static readonly RECT Empty; + + public int Width + { + get { + return Math.Abs(Right - Left); + } // Abs needed for BIDI OS + } + + public int Height + { + get { + return Bottom - Top; + } + } + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(RECT rcSrc) + { + Left = rcSrc.Left; + Top = rcSrc.Top; + Right = rcSrc.Right; + Bottom = rcSrc.Bottom; + } + + public bool IsEmpty + { + get { + // BUGBUG : On Bidi OS (hebrew arabic) left > right + return Left >= Right || Top >= Bottom; + } + } + + public override string ToString() + { + if (this == Empty) + { + return "RECT {Empty}"; + } + return "RECT { left : " + Left + " / top : " + Top + " / right : " + Right + " / bottom : " + + Bottom + " }"; + } + + public override bool Equals(object obj) + { + if (!(obj is RECT)) + { + return false; + } + return (this == (RECT)obj); + } + + public override int GetHashCode() + { + return Left.GetHashCode() + Top.GetHashCode() + Right.GetHashCode() + + Bottom.GetHashCode(); + } + + public static bool operator ==(RECT rect1, RECT rect2) + { + return (rect1.Left == rect2.Left && rect1.Top == rect2.Top && rect1.Right == rect2.Right && + rect1.Bottom == rect2.Bottom); + } + + public static bool operator !=(RECT rect1, RECT rect2) + { + return !(rect1 == rect2); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MINMAXINFO + { + public POINT ptReserved; + + public POINT ptMaxSize; + + public POINT ptMaxPosition; + + public POINT ptMinTrackSize; + + public POINT ptMaxTrackSize; + }; + } +} diff --git a/BrightSharp/Themes/Theme.xaml b/BrightSharp/Themes/Theme.xaml index da1d3a8..11bd21d 100644 --- a/BrightSharp/Themes/Theme.xaml +++ b/BrightSharp/Themes/Theme.xaml @@ -5630,7 +5630,6 @@