Window.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Windows;
  4. using System.Windows.Data;
  5. using System.Windows.Input;
  6. using System.Windows.Media;
  7. using HandyControl.Data;
  8. using HandyControl.Tools;
  9. using HandyControl.Tools.Extension;
  10. using HandyControl.Tools.Interop;
  11. #if NET40
  12. using Microsoft.Windows.Shell;
  13. #else
  14. using System.Windows.Shell;
  15. #endif
  16. namespace HandyControl.Controls
  17. {
  18. [TemplatePart(Name = ElementNonClientArea, Type = typeof(UIElement))]
  19. public class Window : System.Windows.Window
  20. {
  21. #region fields
  22. private const string ElementNonClientArea = "PART_NonClientArea";
  23. private bool _isFullScreen;
  24. private Thickness _actualBorderThickness;
  25. private readonly Thickness _commonPadding;
  26. private bool _showNonClientArea = true;
  27. private double _tempNonClientAreaHeight;
  28. private WindowState _tempWindowState;
  29. private WindowStyle _tempWindowStyle;
  30. private ResizeMode _tempResizeMode;
  31. private UIElement _nonClientArea;
  32. #endregion
  33. #region ctor
  34. static Window()
  35. {
  36. StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata(ResourceHelper.GetResourceInternal<Style>(ResourceToken.WindowWin10)));
  37. }
  38. public Window()
  39. {
  40. #if NET40
  41. var chrome = new WindowChrome
  42. {
  43. CornerRadius = new CornerRadius(),
  44. GlassFrameThickness = new Thickness(0, 0, 0, 1)
  45. };
  46. #else
  47. var chrome = new WindowChrome
  48. {
  49. CornerRadius = new CornerRadius(),
  50. GlassFrameThickness = new Thickness(0, 0, 0, 1),
  51. UseAeroCaptionButtons = false
  52. };
  53. #endif
  54. BindingOperations.SetBinding(chrome, WindowChrome.CaptionHeightProperty,
  55. new Binding(NonClientAreaHeightProperty.Name) { Source = this });
  56. WindowChrome.SetWindowChrome(this, chrome);
  57. _commonPadding = Padding;
  58. Loaded += (s, e) => OnLoaded(e);
  59. }
  60. #endregion
  61. #region prop
  62. public static readonly DependencyProperty NonClientAreaContentProperty = DependencyProperty.Register(
  63. nameof(NonClientAreaContent), typeof(object), typeof(Window), new PropertyMetadata(default(object)));
  64. public object NonClientAreaContent
  65. {
  66. get => GetValue(NonClientAreaContentProperty);
  67. set => SetValue(NonClientAreaContentProperty, value);
  68. }
  69. public static readonly DependencyProperty CloseButtonHoverBackgroundProperty = DependencyProperty.Register(
  70. nameof(CloseButtonHoverBackground), typeof(Brush), typeof(Window),
  71. new PropertyMetadata(default(Brush)));
  72. public Brush CloseButtonHoverBackground
  73. {
  74. get => (Brush) GetValue(CloseButtonHoverBackgroundProperty);
  75. set => SetValue(CloseButtonHoverBackgroundProperty, value);
  76. }
  77. public static readonly DependencyProperty CloseButtonHoverForegroundProperty =
  78. DependencyProperty.Register(
  79. nameof(CloseButtonHoverForeground), typeof(Brush), typeof(Window),
  80. new PropertyMetadata(default(Brush)));
  81. public Brush CloseButtonHoverForeground
  82. {
  83. get => (Brush) GetValue(CloseButtonHoverForegroundProperty);
  84. set => SetValue(CloseButtonHoverForegroundProperty, value);
  85. }
  86. public static readonly DependencyProperty CloseButtonBackgroundProperty = DependencyProperty.Register(
  87. nameof(CloseButtonBackground), typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
  88. public Brush CloseButtonBackground
  89. {
  90. get => (Brush) GetValue(CloseButtonBackgroundProperty);
  91. set => SetValue(CloseButtonBackgroundProperty, value);
  92. }
  93. public static readonly DependencyProperty CloseButtonForegroundProperty = DependencyProperty.Register(
  94. nameof(CloseButtonForeground), typeof(Brush), typeof(Window),
  95. new PropertyMetadata(Brushes.White));
  96. public Brush CloseButtonForeground
  97. {
  98. get => (Brush) GetValue(CloseButtonForegroundProperty);
  99. set => SetValue(CloseButtonForegroundProperty, value);
  100. }
  101. public static readonly DependencyProperty OtherButtonBackgroundProperty = DependencyProperty.Register(
  102. nameof(OtherButtonBackground), typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
  103. public Brush OtherButtonBackground
  104. {
  105. get => (Brush) GetValue(OtherButtonBackgroundProperty);
  106. set => SetValue(OtherButtonBackgroundProperty, value);
  107. }
  108. public static readonly DependencyProperty OtherButtonForegroundProperty = DependencyProperty.Register(
  109. nameof(OtherButtonForeground), typeof(Brush), typeof(Window),
  110. new PropertyMetadata(Brushes.White));
  111. public Brush OtherButtonForeground
  112. {
  113. get => (Brush) GetValue(OtherButtonForegroundProperty);
  114. set => SetValue(OtherButtonForegroundProperty, value);
  115. }
  116. public static readonly DependencyProperty OtherButtonHoverBackgroundProperty = DependencyProperty.Register(
  117. nameof(OtherButtonHoverBackground), typeof(Brush), typeof(Window),
  118. new PropertyMetadata(default(Brush)));
  119. public Brush OtherButtonHoverBackground
  120. {
  121. get => (Brush) GetValue(OtherButtonHoverBackgroundProperty);
  122. set => SetValue(OtherButtonHoverBackgroundProperty, value);
  123. }
  124. public static readonly DependencyProperty OtherButtonHoverForegroundProperty =
  125. DependencyProperty.Register(
  126. nameof(OtherButtonHoverForeground), typeof(Brush), typeof(Window),
  127. new PropertyMetadata(default(Brush)));
  128. public Brush OtherButtonHoverForeground
  129. {
  130. get => (Brush) GetValue(OtherButtonHoverForegroundProperty);
  131. set => SetValue(OtherButtonHoverForegroundProperty, value);
  132. }
  133. public static readonly DependencyProperty NonClientAreaBackgroundProperty = DependencyProperty.Register(
  134. nameof(NonClientAreaBackground), typeof(Brush), typeof(Window),
  135. new PropertyMetadata(default(Brush)));
  136. public Brush NonClientAreaBackground
  137. {
  138. get => (Brush) GetValue(NonClientAreaBackgroundProperty);
  139. set => SetValue(NonClientAreaBackgroundProperty, value);
  140. }
  141. public static readonly DependencyProperty NonClientAreaForegroundProperty = DependencyProperty.Register(
  142. nameof(NonClientAreaForeground), typeof(Brush), typeof(Window),
  143. new PropertyMetadata(default(Brush)));
  144. public Brush NonClientAreaForeground
  145. {
  146. get => (Brush) GetValue(NonClientAreaForegroundProperty);
  147. set => SetValue(NonClientAreaForegroundProperty, value);
  148. }
  149. public static readonly DependencyProperty NonClientAreaHeightProperty = DependencyProperty.Register(
  150. nameof(NonClientAreaHeight), typeof(double), typeof(Window),
  151. new PropertyMetadata(22.0));
  152. public double NonClientAreaHeight
  153. {
  154. get => (double) GetValue(NonClientAreaHeightProperty);
  155. set => SetValue(NonClientAreaHeightProperty, value);
  156. }
  157. public static readonly DependencyProperty ShowNonClientAreaProperty = DependencyProperty.Register(
  158. nameof(ShowNonClientArea), typeof(bool), typeof(Window),
  159. new PropertyMetadata(ValueBoxes.TrueBox, OnShowNonClientAreaChanged));
  160. public bool ShowNonClientArea
  161. {
  162. get => (bool) GetValue(ShowNonClientAreaProperty);
  163. set => SetValue(ShowNonClientAreaProperty, ValueBoxes.BooleanBox(value));
  164. }
  165. public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register(
  166. nameof(ShowTitle), typeof(bool), typeof(Window), new PropertyMetadata(ValueBoxes.TrueBox));
  167. public bool ShowTitle
  168. {
  169. get => (bool) GetValue(ShowTitleProperty);
  170. set => SetValue(ShowTitleProperty, ValueBoxes.BooleanBox(value));
  171. }
  172. public static readonly DependencyProperty IsFullScreenProperty = DependencyProperty.Register(
  173. nameof(IsFullScreen), typeof(bool), typeof(Window),
  174. new PropertyMetadata(ValueBoxes.FalseBox, OnIsFullScreenChanged));
  175. public bool IsFullScreen
  176. {
  177. get => (bool) GetValue(IsFullScreenProperty);
  178. set => SetValue(IsFullScreenProperty, ValueBoxes.BooleanBox(value));
  179. }
  180. public static readonly DependencyProperty ShowIconProperty = DependencyProperty.Register(
  181. nameof(ShowIcon), typeof(bool), typeof(Window), new PropertyMetadata(ValueBoxes.TrueBox));
  182. public bool ShowIcon
  183. {
  184. get => (bool) GetValue(ShowIconProperty);
  185. set => SetValue(ShowIconProperty, value);
  186. }
  187. #endregion
  188. #region methods
  189. #region public
  190. public override void OnApplyTemplate()
  191. {
  192. base.OnApplyTemplate();
  193. _nonClientArea = GetTemplateChild(ElementNonClientArea) as UIElement;
  194. }
  195. #endregion
  196. #region protected
  197. protected override void OnSourceInitialized(EventArgs e)
  198. {
  199. base.OnSourceInitialized(e);
  200. this.GetHwndSource()?.AddHook(HwndSourceHook);
  201. }
  202. protected override void OnStateChanged(EventArgs e)
  203. {
  204. base.OnStateChanged(e);
  205. if (WindowState == WindowState.Maximized)
  206. {
  207. BorderThickness = new Thickness();
  208. _tempNonClientAreaHeight = NonClientAreaHeight;
  209. NonClientAreaHeight += 8;
  210. }
  211. else
  212. {
  213. BorderThickness = _actualBorderThickness;
  214. NonClientAreaHeight = _tempNonClientAreaHeight;
  215. }
  216. }
  217. protected void OnLoaded(RoutedEventArgs args)
  218. {
  219. _actualBorderThickness = BorderThickness;
  220. _tempNonClientAreaHeight = NonClientAreaHeight;
  221. if (WindowState == WindowState.Maximized)
  222. {
  223. BorderThickness = new Thickness();
  224. _tempNonClientAreaHeight += 8;
  225. }
  226. CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand,
  227. (s, e) => WindowState = WindowState.Minimized));
  228. CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand,
  229. (s, e) => WindowState = WindowState.Maximized));
  230. CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand,
  231. (s, e) => WindowState = WindowState.Normal));
  232. CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (s, e) => Close()));
  233. CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
  234. _tempWindowState = WindowState;
  235. _tempWindowStyle = WindowStyle;
  236. _tempResizeMode = ResizeMode;
  237. SwitchIsFullScreen(_isFullScreen);
  238. SwitchShowNonClientArea(_showNonClientArea);
  239. if (WindowState == WindowState.Maximized)
  240. {
  241. _tempNonClientAreaHeight -= 8;
  242. }
  243. if (SizeToContent != SizeToContent.WidthAndHeight)
  244. return;
  245. SizeToContent = SizeToContent.Height;
  246. Dispatcher.BeginInvoke(new Action(() => { SizeToContent = SizeToContent.WidthAndHeight; }));
  247. }
  248. protected override void OnContentRendered(EventArgs e)
  249. {
  250. base.OnContentRendered(e);
  251. if (SizeToContent == SizeToContent.WidthAndHeight)
  252. InvalidateMeasure();
  253. }
  254. #endregion
  255. #region private
  256. private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
  257. {
  258. var mmi = (InteropValues.MINMAXINFO) Marshal.PtrToStructure(lParam, typeof(InteropValues.MINMAXINFO));
  259. var monitor = InteropMethods.MonitorFromWindow(hwnd, InteropValues.MONITOR_DEFAULTTONEAREST);
  260. if (monitor != IntPtr.Zero && mmi != null)
  261. {
  262. InteropValues.APPBARDATA appBarData = default;
  263. var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
  264. if (autoHide)
  265. {
  266. var monitorInfo = default(InteropValues.MONITORINFO);
  267. monitorInfo.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
  268. InteropMethods.GetMonitorInfo(monitor, ref monitorInfo);
  269. var rcWorkArea = monitorInfo.rcWork;
  270. var rcMonitorArea = monitorInfo.rcMonitor;
  271. mmi.ptMaxPosition.X = Math.Abs(rcWorkArea.Left - rcMonitorArea.Left);
  272. mmi.ptMaxPosition.Y = Math.Abs(rcWorkArea.Top - rcMonitorArea.Top);
  273. mmi.ptMaxSize.X = Math.Abs(rcWorkArea.Right - rcWorkArea.Left);
  274. mmi.ptMaxSize.Y = Math.Abs(rcWorkArea.Bottom - rcWorkArea.Top - 1);
  275. }
  276. }
  277. Marshal.StructureToPtr(mmi, lParam, true);
  278. }
  279. private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
  280. {
  281. switch (msg)
  282. {
  283. case InteropValues.WM_WINDOWPOSCHANGED:
  284. Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
  285. break;
  286. case InteropValues.WM_GETMINMAXINFO:
  287. WmGetMinMaxInfo(hwnd, lparam);
  288. Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
  289. break;
  290. case InteropValues.WM_NCHITTEST:
  291. // for fixing #886
  292. // https://developercommunity.visualstudio.com/t/overflow-exception-in-windowchrome/167357
  293. try
  294. {
  295. _ = lparam.ToInt32();
  296. }
  297. catch (OverflowException)
  298. {
  299. handled = true;
  300. }
  301. break;
  302. }
  303. return IntPtr.Zero;
  304. }
  305. private static void OnShowNonClientAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  306. {
  307. var ctl = (Window) d;
  308. ctl.SwitchShowNonClientArea((bool) e.NewValue);
  309. }
  310. private void SwitchShowNonClientArea(bool showNonClientArea)
  311. {
  312. if (_nonClientArea == null)
  313. {
  314. _showNonClientArea = showNonClientArea;
  315. return;
  316. }
  317. if (showNonClientArea)
  318. {
  319. if (IsFullScreen)
  320. {
  321. _nonClientArea.Show(false);
  322. _tempNonClientAreaHeight = NonClientAreaHeight;
  323. NonClientAreaHeight = 0;
  324. }
  325. else
  326. {
  327. _nonClientArea.Show(true);
  328. NonClientAreaHeight = _tempNonClientAreaHeight;
  329. }
  330. }
  331. else
  332. {
  333. _nonClientArea.Show(false);
  334. _tempNonClientAreaHeight = NonClientAreaHeight;
  335. NonClientAreaHeight = 0;
  336. }
  337. }
  338. private static void OnIsFullScreenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  339. {
  340. var ctl = (Window) d;
  341. ctl.SwitchIsFullScreen((bool) e.NewValue);
  342. }
  343. private void SwitchIsFullScreen(bool isFullScreen)
  344. {
  345. if (_nonClientArea == null)
  346. {
  347. _isFullScreen = isFullScreen;
  348. return;
  349. }
  350. if (isFullScreen)
  351. {
  352. _nonClientArea.Show(false);
  353. _tempNonClientAreaHeight = NonClientAreaHeight;
  354. NonClientAreaHeight = 0;
  355. _tempWindowState = WindowState;
  356. _tempWindowStyle = WindowStyle;
  357. _tempResizeMode = ResizeMode;
  358. WindowStyle = WindowStyle.None;
  359. //下面三行不能改变,就是故意的
  360. WindowState = WindowState.Maximized;
  361. WindowState = WindowState.Minimized;
  362. WindowState = WindowState.Maximized;
  363. }
  364. else
  365. {
  366. if (ShowNonClientArea)
  367. {
  368. _nonClientArea.Show(true);
  369. NonClientAreaHeight = _tempNonClientAreaHeight;
  370. }
  371. else
  372. {
  373. _nonClientArea.Show(false);
  374. _tempNonClientAreaHeight = NonClientAreaHeight;
  375. NonClientAreaHeight = 0;
  376. }
  377. WindowState = _tempWindowState;
  378. WindowStyle = _tempWindowStyle;
  379. ResizeMode = _tempResizeMode;
  380. }
  381. }
  382. private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
  383. {
  384. var point = WindowState == WindowState.Maximized
  385. ? new Point(0, NonClientAreaHeight)
  386. : new Point(Left, Top + NonClientAreaHeight);
  387. SystemCommands.ShowSystemMenu(this, point);
  388. }
  389. #endregion
  390. #endregion
  391. }
  392. }