Files
HandyControl/src/Shared/HandyControl_Shared/Controls/Window/Window.cs
T

479 lines
17 KiB
C#

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
using HandyControl.Tools.Interop;
#if NET40
using Microsoft.Windows.Shell;
#else
using System.Windows.Shell;
#endif
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementNonClientArea, Type = typeof(UIElement))]
public class Window : System.Windows.Window
{
#region fields
private const string ElementNonClientArea = "PART_NonClientArea";
private bool _isFullScreen;
private Thickness _actualBorderThickness;
private readonly Thickness _commonPadding;
private bool _showNonClientArea = true;
private double _tempNonClientAreaHeight;
private WindowState _tempWindowState;
private WindowStyle _tempWindowStyle;
private ResizeMode _tempResizeMode;
private UIElement _nonClientArea;
#endregion
#region ctor
static Window()
{
StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata(ResourceHelper.GetResourceInternal<Style>(ResourceToken.WindowWin10)));
}
public Window()
{
#if NET40
var chrome = new WindowChrome
{
CornerRadius = new CornerRadius(),
GlassFrameThickness = new Thickness(0, 0, 0, 1)
};
#else
var chrome = new WindowChrome
{
CornerRadius = new CornerRadius(),
GlassFrameThickness = new Thickness(0, 0, 0, 1),
UseAeroCaptionButtons = false
};
#endif
BindingOperations.SetBinding(chrome, WindowChrome.CaptionHeightProperty,
new Binding(NonClientAreaHeightProperty.Name) { Source = this });
WindowChrome.SetWindowChrome(this, chrome);
_commonPadding = Padding;
Loaded += (s, e) => OnLoaded(e);
}
#endregion
#region prop
public static readonly DependencyProperty NonClientAreaContentProperty = DependencyProperty.Register(
"NonClientAreaContent", typeof(object), typeof(Window), new PropertyMetadata(default(object)));
public object NonClientAreaContent
{
get => GetValue(NonClientAreaContentProperty);
set => SetValue(NonClientAreaContentProperty, value);
}
public static readonly DependencyProperty CloseButtonHoverBackgroundProperty = DependencyProperty.Register(
"CloseButtonHoverBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush CloseButtonHoverBackground
{
get => (Brush) GetValue(CloseButtonHoverBackgroundProperty);
set => SetValue(CloseButtonHoverBackgroundProperty, value);
}
public static readonly DependencyProperty CloseButtonHoverForegroundProperty =
DependencyProperty.Register(
"CloseButtonHoverForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush CloseButtonHoverForeground
{
get => (Brush) GetValue(CloseButtonHoverForegroundProperty);
set => SetValue(CloseButtonHoverForegroundProperty, value);
}
public static readonly DependencyProperty CloseButtonBackgroundProperty = DependencyProperty.Register(
"CloseButtonBackground", typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
public Brush CloseButtonBackground
{
get => (Brush) GetValue(CloseButtonBackgroundProperty);
set => SetValue(CloseButtonBackgroundProperty, value);
}
public static readonly DependencyProperty CloseButtonForegroundProperty = DependencyProperty.Register(
"CloseButtonForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(Brushes.White));
public Brush CloseButtonForeground
{
get => (Brush) GetValue(CloseButtonForegroundProperty);
set => SetValue(CloseButtonForegroundProperty, value);
}
public static readonly DependencyProperty OtherButtonBackgroundProperty = DependencyProperty.Register(
"OtherButtonBackground", typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
public Brush OtherButtonBackground
{
get => (Brush) GetValue(OtherButtonBackgroundProperty);
set => SetValue(OtherButtonBackgroundProperty, value);
}
public static readonly DependencyProperty OtherButtonForegroundProperty = DependencyProperty.Register(
"OtherButtonForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(Brushes.White));
public Brush OtherButtonForeground
{
get => (Brush) GetValue(OtherButtonForegroundProperty);
set => SetValue(OtherButtonForegroundProperty, value);
}
public static readonly DependencyProperty OtherButtonHoverBackgroundProperty = DependencyProperty.Register(
"OtherButtonHoverBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush OtherButtonHoverBackground
{
get => (Brush) GetValue(OtherButtonHoverBackgroundProperty);
set => SetValue(OtherButtonHoverBackgroundProperty, value);
}
public static readonly DependencyProperty OtherButtonHoverForegroundProperty =
DependencyProperty.Register(
"OtherButtonHoverForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush OtherButtonHoverForeground
{
get => (Brush) GetValue(OtherButtonHoverForegroundProperty);
set => SetValue(OtherButtonHoverForegroundProperty, value);
}
public static readonly DependencyProperty NonClientAreaBackgroundProperty = DependencyProperty.Register(
"NonClientAreaBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush NonClientAreaBackground
{
get => (Brush) GetValue(NonClientAreaBackgroundProperty);
set => SetValue(NonClientAreaBackgroundProperty, value);
}
public static readonly DependencyProperty NonClientAreaForegroundProperty = DependencyProperty.Register(
"NonClientAreaForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public Brush NonClientAreaForeground
{
get => (Brush) GetValue(NonClientAreaForegroundProperty);
set => SetValue(NonClientAreaForegroundProperty, value);
}
public static readonly DependencyProperty NonClientAreaHeightProperty = DependencyProperty.Register(
"NonClientAreaHeight", typeof(double), typeof(Window),
new PropertyMetadata(22.0));
public double NonClientAreaHeight
{
get => (double) GetValue(NonClientAreaHeightProperty);
set => SetValue(NonClientAreaHeightProperty, value);
}
public static readonly DependencyProperty ShowNonClientAreaProperty = DependencyProperty.Register(
"ShowNonClientArea", typeof(bool), typeof(Window),
new PropertyMetadata(ValueBoxes.TrueBox, OnShowNonClientAreaChanged));
public bool ShowNonClientArea
{
get => (bool) GetValue(ShowNonClientAreaProperty);
set => SetValue(ShowNonClientAreaProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register(
"ShowTitle", typeof(bool), typeof(Window), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowTitle
{
get => (bool) GetValue(ShowTitleProperty);
set => SetValue(ShowTitleProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty IsFullScreenProperty = DependencyProperty.Register(
"IsFullScreen", typeof(bool), typeof(Window),
new PropertyMetadata(ValueBoxes.FalseBox, OnIsFullScreenChanged));
public bool IsFullScreen
{
get => (bool) GetValue(IsFullScreenProperty);
set => SetValue(IsFullScreenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowIconProperty = DependencyProperty.Register(
"ShowIcon", typeof(bool), typeof(Window), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowIcon
{
get => (bool) GetValue(ShowIconProperty);
set => SetValue(ShowIconProperty, value);
}
#endregion
#region methods
#region public
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_nonClientArea = GetTemplateChild(ElementNonClientArea) as UIElement;
}
#endregion
#region protected
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
this.GetHwndSource()?.AddHook(HwndSourceHook);
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
if (WindowState == WindowState.Maximized)
{
BorderThickness = new Thickness();
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight += 8;
}
else
{
BorderThickness = _actualBorderThickness;
NonClientAreaHeight = _tempNonClientAreaHeight;
}
}
protected void OnLoaded(RoutedEventArgs args)
{
_actualBorderThickness = BorderThickness;
_tempNonClientAreaHeight = NonClientAreaHeight;
if (WindowState == WindowState.Maximized)
{
BorderThickness = new Thickness();
_tempNonClientAreaHeight += 8;
}
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand,
(s, e) => WindowState = WindowState.Minimized));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand,
(s, e) => WindowState = WindowState.Maximized));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand,
(s, e) => WindowState = WindowState.Normal));
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (s, e) => Close()));
CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
_tempWindowState = WindowState;
_tempWindowStyle = WindowStyle;
_tempResizeMode = ResizeMode;
SwitchIsFullScreen(_isFullScreen);
SwitchShowNonClientArea(_showNonClientArea);
if (WindowState == WindowState.Maximized)
{
_tempNonClientAreaHeight -= 8;
}
if (SizeToContent != SizeToContent.WidthAndHeight)
return;
SizeToContent = SizeToContent.Height;
Dispatcher.BeginInvoke(new Action(() => { SizeToContent = SizeToContent.WidthAndHeight; }));
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (SizeToContent == SizeToContent.WidthAndHeight)
InvalidateMeasure();
}
#endregion
#region private
private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
var mmi = (InteropValues.MINMAXINFO) Marshal.PtrToStructure(lParam, typeof(InteropValues.MINMAXINFO));
var monitor = InteropMethods.MonitorFromWindow(hwnd, InteropValues.MONITOR_DEFAULTTONEAREST);
if (monitor != IntPtr.Zero && mmi != null)
{
InteropValues.APPBARDATA appBarData = default;
var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
if (autoHide)
{
var monitorInfo = default(InteropValues.MONITORINFO);
monitorInfo.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
InteropMethods.GetMonitorInfo(monitor, ref monitorInfo);
var rcWorkArea = monitorInfo.rcWork;
var 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 - 1);
}
}
Marshal.StructureToPtr(mmi, lParam, true);
}
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
switch (msg)
{
case InteropValues.WM_WINDOWPOSCHANGED:
Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
break;
case InteropValues.WM_GETMINMAXINFO:
WmGetMinMaxInfo(hwnd, lparam);
Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
break;
case InteropValues.WM_NCHITTEST:
// for fixing #886
// https://developercommunity.visualstudio.com/t/overflow-exception-in-windowchrome/167357
try
{
_ = lparam.ToInt32();
}
catch (OverflowException)
{
handled = true;
}
break;
}
return IntPtr.Zero;
}
private static void OnShowNonClientAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Window) d;
ctl.SwitchShowNonClientArea((bool) e.NewValue);
}
private void SwitchShowNonClientArea(bool showNonClientArea)
{
if (_nonClientArea == null)
{
_showNonClientArea = showNonClientArea;
return;
}
if (showNonClientArea)
{
if (IsFullScreen)
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
else
{
_nonClientArea.Show(true);
NonClientAreaHeight = _tempNonClientAreaHeight;
}
}
else
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
}
private static void OnIsFullScreenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Window) d;
ctl.SwitchIsFullScreen((bool) e.NewValue);
}
private void SwitchIsFullScreen(bool isFullScreen)
{
if (_nonClientArea == null)
{
_isFullScreen = isFullScreen;
return;
}
if (isFullScreen)
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
_tempWindowState = WindowState;
_tempWindowStyle = WindowStyle;
_tempResizeMode = ResizeMode;
WindowStyle = WindowStyle.None;
//下面三行不能改变,就是故意的
WindowState = WindowState.Maximized;
WindowState = WindowState.Minimized;
WindowState = WindowState.Maximized;
}
else
{
if (ShowNonClientArea)
{
_nonClientArea.Show(true);
NonClientAreaHeight = _tempNonClientAreaHeight;
}
else
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
WindowState = _tempWindowState;
WindowStyle = _tempWindowStyle;
ResizeMode = _tempResizeMode;
}
}
private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var point = WindowState == WindowState.Maximized
? new Point(0, NonClientAreaHeight)
: new Point(Left, Top + NonClientAreaHeight);
SystemCommands.ShowSystemMenu(this, point);
}
#endregion
#endregion
}
}