WindowManager.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. using System.Runtime.InteropServices;
  2. using System.Windows.Forms.VisualStyles;
  3. using System.Windows.Input;
  4. namespace Caliburn.Micro {
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Controls.Primitives;
  11. using System.Windows.Data;
  12. using System.Linq;
  13. using System.Windows.Navigation;
  14. using Caliburn.Micro.Core;
  15. using System.Windows.Media;
  16. using OpenSEMI.Ctrlib.Window;
  17. /// <summary>
  18. /// A service that manages windows.
  19. /// </summary>
  20. public interface IWindowManager {
  21. /// <summary>
  22. /// Shows a modal dialog for the specified model.
  23. /// </summary>
  24. /// <param name="rootModel">The root model.</param>
  25. /// <param name="context">The context.</param>
  26. /// <param name="settings">The optional dialog settings.</param>
  27. /// <returns>The dialog result.</returns>
  28. bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
  29. /// <summary>
  30. /// Shows a non-modal window for the specified model.
  31. /// </summary>
  32. /// <param name="rootModel">The root model.</param>
  33. /// <param name="context">The context.</param>
  34. /// <param name="settings">The optional window settings.</param>
  35. void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
  36. /// <summary>
  37. /// Shows a popup at the current mouse position.
  38. /// </summary>
  39. /// <param name="rootModel">The root model.</param>
  40. /// <param name="context">The view context.</param>
  41. /// <param name="settings">The optional popup settings.</param>
  42. void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
  43. }
  44. /// <summary>
  45. /// A service that manages windows.
  46. /// </summary>
  47. public class WindowManager : IWindowManager {
  48. /// <summary>
  49. /// Shows a modal dialog for the specified model.
  50. /// </summary>
  51. /// <param name="rootModel">The root model.</param>
  52. /// <param name="context">The context.</param>
  53. /// <param name="settings">The dialog popup settings.</param>
  54. /// <returns>The dialog result.</returns>
  55. public virtual bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null){
  56. Window window = CreateWindow(rootModel, true, context, settings);
  57. window.ShowInTaskbar = false;
  58. window.ResizeMode = ResizeMode.NoResize;
  59. window.WindowStyle = WindowStyle.SingleBorderWindow;
  60. return window.ShowDialog();
  61. }
  62. public virtual bool? ShowDialog(object rootModel, Point position, object context = null, IDictionary<string, object> settings = null)
  63. {
  64. Window window = CreateWindow(rootModel, true, context, settings);
  65. window.ShowInTaskbar = false;
  66. window.ResizeMode = ResizeMode.NoResize;
  67. window.WindowStyle = WindowStyle.SingleBorderWindow;
  68. window.WindowStartupLocation = WindowStartupLocation.Manual;
  69. window.Owner = Application.Current.MainWindow;
  70. window.Left = position.X;
  71. window.Top = position.Y;
  72. return window.ShowDialog();
  73. }
  74. public virtual bool? ShowDialog(object rootModel, Window owner, object context = null, IDictionary<string, object> settings = null)
  75. {
  76. Window window = CreateWindow(rootModel, true, context, settings);
  77. window.ShowInTaskbar = false;
  78. window.ResizeMode = ResizeMode.NoResize;
  79. window.WindowStyle = WindowStyle.SingleBorderWindow;
  80. window.Owner = owner;
  81. return window.ShowDialog();
  82. }
  83. public virtual bool? ShowDialog(object rootModel, WindowState pWindowState , object context = null, IDictionary<string, object> settings = null)
  84. {
  85. Window window = CreateWindow(rootModel, true, context, settings);
  86. window.ShowInTaskbar = false;
  87. window.ResizeMode = ResizeMode.CanResize;
  88. window.WindowStyle = WindowStyle.SingleBorderWindow;
  89. window.WindowState = pWindowState;
  90. return window.ShowDialog();
  91. }
  92. public virtual bool? ShowDialogWithNoWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
  93. {
  94. Window window = CreateWindow(rootModel, true, context, settings);
  95. window.ShowInTaskbar = false;
  96. window.ResizeMode = ResizeMode.NoResize;
  97. window.WindowStyle = WindowStyle.None;
  98. window.BorderThickness = new Thickness(1);
  99. window.BorderBrush = new SolidColorBrush(Colors.Black);
  100. return window.ShowDialog();
  101. }
  102. /// <summary>
  103. /// Shows a special modal dialog for the specified model.
  104. /// </summary>
  105. /// <param name="rootModel">The root model.</param>
  106. /// <param name="context">The context.</param>
  107. /// <returns>The dialog result.</returns>
  108. public virtual bool? ShowDialogWithNoStyle(object rootModel, object context = null)
  109. {
  110. Window window = CreateWindow(rootModel, true, context);
  111. window.WindowStyle = WindowStyle.None;
  112. window.AllowsTransparency = true;
  113. window.ResizeMode = ResizeMode.NoResize;
  114. window.Background = new SolidColorBrush(Colors.Transparent);
  115. window.ShowInTaskbar = false;
  116. window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  117. window.Topmost = true;
  118. return window.ShowDialog();
  119. }
  120. /// <summary>
  121. /// Shows a special modal dialog for the specified model.
  122. /// </summary>
  123. /// <param name="rootModel">The root model.</param>
  124. /// <param name="context">The context.</param>
  125. /// <returns>The dialog result.</returns>
  126. public virtual bool? ShowDialogWithTitle(object rootModel, object context = null, string title="")
  127. {
  128. Window window = CreateWindow(rootModel, true, context);
  129. window.ShowInTaskbar = false;
  130. window.ResizeMode = ResizeMode.NoResize;
  131. window.WindowStyle = WindowStyle.SingleBorderWindow;
  132. window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  133. window.Title = title;
  134. return window.ShowDialog();
  135. }
  136. /// <summary>
  137. /// Shows a special modal dialog for the specified model.
  138. /// </summary>
  139. /// <param name="rootModel">The root model.</param>
  140. /// <param name="context">The context.</param>
  141. /// <returns>The dialog result.</returns>
  142. public virtual bool? ShowCustomWndDialogWithTitle(object rootModel, object context = null, string title = "")
  143. {
  144. CustomWnd window = CreateCustomWnd(rootModel, true, context);
  145. window.ShowInTaskbar = false;
  146. window.ResizeMode = ResizeMode.NoResize;
  147. window.WindowStyle = WindowStyle.SingleBorderWindow;
  148. window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  149. window.Title = title;
  150. return window.ShowDialog();
  151. }
  152. /// <summary>
  153. /// Creates a window.
  154. /// </summary>
  155. /// <param name="rootModel">The view model.</param>
  156. /// <param name="isDialog">Whethor or not the window is being shown as a dialog.</param>
  157. /// <param name="context">The view context.</param>
  158. /// <param name="createCompletedCallBack">Call back and return the new window when create window completed</param>
  159. /// <returns>The window.</returns>
  160. protected virtual Window CreateWindow(object rootModel, bool isDialog, object context, Action<Window> createCompletedCallBack = null)
  161. {
  162. var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context), isDialog);
  163. ViewModelBinder.Bind(rootModel, view, context);
  164. var haveDisplayName = rootModel as IHaveDisplayName;
  165. if (haveDisplayName != null && !ConventionManager.HasBinding(view, Window.TitleProperty))
  166. {
  167. var binding = new Binding("DisplayName") { Mode = BindingMode.OneWay };
  168. view.SetBinding(Window.TitleProperty, binding);
  169. }
  170. if (createCompletedCallBack != null)
  171. {
  172. createCompletedCallBack(view);
  173. }
  174. new WindowConductor(rootModel, view);
  175. return view;
  176. }
  177. /// <summary>
  178. /// Creates a window.
  179. /// </summary>
  180. /// <param name="rootModel">The view model.</param>
  181. /// <param name="isDialog">Whethor or not the window is being shown as a dialog.</param>
  182. /// <param name="context">The view context.</param>
  183. /// <param name="createCompletedCallBack">Call back and return the new window when create window completed</param>
  184. /// <returns>The window.</returns>
  185. protected virtual CustomWnd CreateCustomWnd(object rootModel, bool isDialog, object context, Action<Window> createCompletedCallBack = null)
  186. {
  187. var view = EnsureCustomWnd(rootModel, ViewLocator.LocateForModel(rootModel, null, context), isDialog);
  188. ViewModelBinder.Bind(rootModel, view, context);
  189. var haveDisplayName = rootModel as IHaveDisplayName;
  190. if (haveDisplayName != null && !ConventionManager.HasBinding(view, Window.TitleProperty))
  191. {
  192. var binding = new Binding("DisplayName") { Mode = BindingMode.OneWay };
  193. view.SetBinding(CustomWnd.TitleProperty, binding);
  194. }
  195. if (createCompletedCallBack != null)
  196. {
  197. createCompletedCallBack(view);
  198. }
  199. new WindowConductor(rootModel, view);
  200. return view;
  201. }
  202. /// <summary>
  203. /// Makes sure the view is a window is is wrapped by one.
  204. /// </summary>
  205. /// <param name="model">The view model.</param>
  206. /// <param name="view">The view.</param>
  207. /// <param name="isDialog">Whethor or not the window is being shown as a dialog.</param>
  208. /// <returns>The window.</returns>
  209. protected virtual CustomWnd EnsureCustomWnd(object model, object view, bool isDialog)
  210. {
  211. var window = view as CustomWnd;
  212. if (window == null)
  213. {
  214. window = new CustomWnd
  215. {
  216. Content = view,
  217. SizeToContent = SizeToContent.WidthAndHeight
  218. };
  219. window.SetValue(View.IsGeneratedProperty, true);
  220. var owner = CustomWndInferOwnerOf(window);
  221. if (owner != null)
  222. {
  223. window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
  224. window.Owner = owner;
  225. }
  226. else
  227. {
  228. window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  229. }
  230. }
  231. else
  232. {
  233. var owner = CustomWndInferOwnerOf(window);
  234. if (owner != null && isDialog)
  235. {
  236. window.Owner = owner;
  237. }
  238. }
  239. return window;
  240. }
  241. /// <summary>
  242. /// Infers the owner of the window.
  243. /// </summary>
  244. /// <param name="window">The window to whose owner needs to be determined.</param>
  245. /// <returns>The owner.</returns>
  246. protected virtual CustomWnd CustomWndInferOwnerOf(CustomWnd window)
  247. {
  248. var application = Application.Current;
  249. if (application == null)
  250. {
  251. return null;
  252. }
  253. var active = application.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
  254. active = active ?? (PresentationSource.FromVisual(application.MainWindow) == null ? null : application.MainWindow);
  255. return (CustomWnd)(active == window ? null : active);
  256. }
  257. /// <summary>
  258. /// Shows a window for the specified model.
  259. /// </summary>
  260. /// <param name="rootModel">The root model.</param>
  261. /// <param name="context">The context.</param>
  262. /// <param name="settings">The optional window settings.</param>
  263. public virtual void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null){
  264. NavigationWindow navWindow = null;
  265. var application = Application.Current;
  266. if (application != null && application.MainWindow != null) {
  267. navWindow = application.MainWindow as NavigationWindow;
  268. }
  269. if(navWindow != null) {
  270. var window = CreatePage(rootModel, context, settings);
  271. navWindow.Navigate(window);
  272. }
  273. else {
  274. CreateWindow(rootModel, false, context, settings).Show();
  275. }
  276. }
  277. /// <summary>
  278. /// Shows a popup at the current mouse position.
  279. /// </summary>
  280. /// <param name="rootModel">The root model.</param>
  281. /// <param name="context">The view context.</param>
  282. /// <param name="settings">The optional popup settings.</param>
  283. public virtual void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null) {
  284. var popup = CreatePopup(rootModel, settings);
  285. var view = ViewLocator.LocateForModel(rootModel, popup, context);
  286. popup.Child = view;
  287. popup.SetValue(View.IsGeneratedProperty, true);
  288. ViewModelBinder.Bind(rootModel, popup, null);
  289. Action.SetTargetWithoutContext(view, rootModel);
  290. var activatable = rootModel as IActivate;
  291. if (activatable != null) {
  292. activatable.Activate();
  293. }
  294. var deactivator = rootModel as IDeactivate;
  295. if (deactivator != null) {
  296. popup.Closed += delegate { deactivator.Deactivate(true); };
  297. }
  298. popup.IsOpen = true;
  299. popup.CaptureMouse();
  300. }
  301. /// <summary>
  302. /// Creates a popup for hosting a popup window.
  303. /// </summary>
  304. /// <param name="rootModel">The model.</param>
  305. /// <param name="settings">The optional popup settings.</param>
  306. /// <returns>The popup.</returns>
  307. protected virtual Popup CreatePopup(object rootModel, IDictionary<string, object> settings) {
  308. var popup = new Popup();
  309. if (ApplySettings(popup, settings)) {
  310. if (!settings.ContainsKey("PlacementTarget") && !settings.ContainsKey("Placement"))
  311. popup.Placement = PlacementMode.MousePoint;
  312. if (!settings.ContainsKey("AllowsTransparency"))
  313. popup.AllowsTransparency = true;
  314. }else {
  315. popup.AllowsTransparency = true;
  316. popup.Placement = PlacementMode.MousePoint;
  317. }
  318. return popup;
  319. }
  320. /// <summary>
  321. /// Creates a window.
  322. /// </summary>
  323. /// <param name="rootModel">The view model.</param>
  324. /// <param name="isDialog">Whethor or not the window is being shown as a dialog.</param>
  325. /// <param name="context">The view context.</param>
  326. /// <param name="settings">The optional popup settings.</param>
  327. /// <returns>The window.</returns>
  328. protected virtual Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings) {
  329. var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context), isDialog);
  330. ViewModelBinder.Bind(rootModel, view, context);
  331. var haveDisplayName = rootModel as IHaveDisplayName;
  332. if (haveDisplayName != null && !ConventionManager.HasBinding(view, Window.TitleProperty)) {
  333. var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
  334. view.SetBinding(Window.TitleProperty, binding);
  335. }
  336. ApplySettings(view, settings);
  337. new WindowConductor(rootModel, view);
  338. return view;
  339. }
  340. /// <summary>
  341. /// Makes sure the view is a window is is wrapped by one.
  342. /// </summary>
  343. /// <param name="model">The view model.</param>
  344. /// <param name="view">The view.</param>
  345. /// <param name="isDialog">Whethor or not the window is being shown as a dialog.</param>
  346. /// <returns>The window.</returns>
  347. protected virtual Window EnsureWindow(object model, object view, bool isDialog) {
  348. var window = view as Window;
  349. if (window == null) {
  350. window = new Window {
  351. Content = view,
  352. SizeToContent = SizeToContent.WidthAndHeight
  353. };
  354. window.Width = ((UserControl)view).Width;
  355. window.Height = ((UserControl)view).Height;
  356. window.SetValue(View.IsGeneratedProperty, true);
  357. var owner = InferOwnerOf(window);
  358. if (owner != null) {
  359. window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
  360. window.Owner = owner;
  361. }
  362. else {
  363. window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  364. }
  365. }
  366. else {
  367. var owner = InferOwnerOf(window);
  368. if (owner != null && isDialog) {
  369. window.Owner = owner;
  370. }
  371. }
  372. return window;
  373. }
  374. /// <summary>
  375. /// Infers the owner of the window.
  376. /// </summary>
  377. /// <param name="window">The window to whose owner needs to be determined.</param>
  378. /// <returns>The owner.</returns>
  379. protected virtual Window InferOwnerOf(Window window) {
  380. var application = Application.Current;
  381. if (application == null) {
  382. return null;
  383. }
  384. var active = application.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
  385. active = active ?? (PresentationSource.FromVisual(application.MainWindow) == null ? null : application.MainWindow);
  386. return active == window ? null : active;
  387. }
  388. /// <summary>
  389. /// Creates the page.
  390. /// </summary>
  391. /// <param name="rootModel">The root model.</param>
  392. /// <param name="context">The context.</param>
  393. /// <param name="settings">The optional popup settings.</param>
  394. /// <returns>The page.</returns>
  395. public virtual Page CreatePage(object rootModel, object context, IDictionary<string, object> settings) {
  396. var view = EnsurePage(rootModel, ViewLocator.LocateForModel(rootModel, null, context));
  397. ViewModelBinder.Bind(rootModel, view, context);
  398. var haveDisplayName = rootModel as IHaveDisplayName;
  399. if (haveDisplayName != null && !ConventionManager.HasBinding(view, Page.TitleProperty)) {
  400. var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
  401. view.SetBinding(Page.TitleProperty, binding);
  402. }
  403. ApplySettings(view, settings);
  404. var activatable = rootModel as IActivate;
  405. if (activatable != null) {
  406. activatable.Activate();
  407. }
  408. var deactivatable = rootModel as IDeactivate;
  409. if (deactivatable != null) {
  410. view.Unloaded += (s, e) => deactivatable.Deactivate(true);
  411. }
  412. return view;
  413. }
  414. /// <summary>
  415. /// Ensures the view is a page or provides one.
  416. /// </summary>
  417. /// <param name="model">The model.</param>
  418. /// <param name="view">The view.</param>
  419. /// <returns>The page.</returns>
  420. protected virtual Page EnsurePage(object model, object view) {
  421. var page = view as Page;
  422. if(page == null) {
  423. page = new Page { Content = view };
  424. page.SetValue(View.IsGeneratedProperty, true);
  425. }
  426. return page;
  427. }
  428. bool ApplySettings(object target, IEnumerable<KeyValuePair<string, object>> settings) {
  429. if (settings != null) {
  430. var type = target.GetType();
  431. foreach (var pair in settings) {
  432. var propertyInfo = type.GetProperty(pair.Key);
  433. if (propertyInfo != null) {
  434. propertyInfo.SetValue(target, pair.Value, null);
  435. }
  436. }
  437. return true;
  438. }
  439. return false;
  440. }
  441. class WindowConductor {
  442. bool deactivatingFromView;
  443. bool deactivateFromViewModel;
  444. bool actuallyClosing;
  445. readonly Window view;
  446. readonly object model;
  447. public WindowConductor(object model, Window view) {
  448. this.model = model;
  449. this.view = view;
  450. var activatable = model as IActivate;
  451. if (activatable != null) {
  452. activatable.Activate();
  453. }
  454. var deactivatable = model as IDeactivate;
  455. if (deactivatable != null) {
  456. view.Closed += Closed;
  457. deactivatable.Deactivated += Deactivated;
  458. }
  459. var guard = model as IGuardClose;
  460. if (guard != null) {
  461. view.Closing += Closing;
  462. }
  463. }
  464. void Closed(object sender, EventArgs e) {
  465. view.Closed -= Closed;
  466. view.Closing -= Closing;
  467. if (deactivateFromViewModel) {
  468. return;
  469. }
  470. var deactivatable = (IDeactivate)model;
  471. deactivatingFromView = true;
  472. deactivatable.Deactivate(true);
  473. deactivatingFromView = false;
  474. }
  475. void Deactivated(object sender, DeactivationEventArgs e) {
  476. if (!e.WasClosed) {
  477. return;
  478. }
  479. ((IDeactivate)model).Deactivated -= Deactivated;
  480. if (deactivatingFromView) {
  481. return;
  482. }
  483. deactivateFromViewModel = true;
  484. actuallyClosing = true;
  485. view.Close();
  486. actuallyClosing = false;
  487. deactivateFromViewModel = false;
  488. }
  489. void Closing(object sender, CancelEventArgs e) {
  490. if (e.Cancel) {
  491. return;
  492. }
  493. var guard = (IGuardClose)model;
  494. if (actuallyClosing) {
  495. actuallyClosing = false;
  496. return;
  497. }
  498. bool runningAsync = false, shouldEnd = false;
  499. guard.CanClose(canClose => {
  500. Execute.OnUIThread(() => {
  501. if(runningAsync && canClose) {
  502. actuallyClosing = true;
  503. view.Close();
  504. }
  505. else {
  506. e.Cancel = !canClose;
  507. }
  508. shouldEnd = true;
  509. });
  510. });
  511. if (shouldEnd) {
  512. return;
  513. }
  514. runningAsync = e.Cancel = true;
  515. }
  516. }
  517. }
  518. public static class WindowExtensions
  519. {
  520. #region Public Methods
  521. public static bool ActivateCenteredToMouse(this Window window)
  522. {
  523. ComputeTopLeft(ref window);
  524. return window.Activate();
  525. }
  526. public static void ShowCenteredToMouse(this Window window)
  527. {
  528. // in case the default start-up location isn't set to Manual
  529. WindowStartupLocation oldLocation = window.WindowStartupLocation;
  530. // set location to manual -> window will be placed by Top and Left property
  531. window.WindowStartupLocation = WindowStartupLocation.Manual;
  532. ComputeTopLeft(ref window);
  533. window.ShowDialog();
  534. window.WindowStartupLocation = oldLocation;
  535. }
  536. #endregion
  537. #region Methods
  538. private static void ComputeTopLeft(ref Window window)
  539. {
  540. W32Point pt = new W32Point();
  541. if (!GetCursorPos(ref pt))
  542. {
  543. Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
  544. }
  545. // 0x00000002: return nearest monitor if pt is not contained in any monitor.
  546. IntPtr monHandle = MonitorFromPoint(pt, 0x00000002);
  547. W32MonitorInfo monInfo = new W32MonitorInfo();
  548. monInfo.Size = Marshal.SizeOf(typeof(W32MonitorInfo));
  549. if (!GetMonitorInfo(monHandle, ref monInfo))
  550. {
  551. Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
  552. }
  553. // use WorkArea struct to include the taskbar position.
  554. W32Rect monitor = monInfo.WorkArea;
  555. double offsetX = Math.Round(window.Width / 2);
  556. double offsetY = Math.Round(window.Height / 2);
  557. double top = pt.Y - offsetY;
  558. double left = pt.X - offsetX;
  559. Rect screen = new Rect(
  560. new Point(monitor.Left, monitor.Top),
  561. new Point(monitor.Right, monitor.Bottom));
  562. Rect wnd = new Rect(
  563. new Point(left, top),
  564. new Point(left + window.Width, top + window.Height));
  565. window.Top = wnd.Top;
  566. window.Left = wnd.Left;
  567. if (!screen.Contains(wnd))
  568. {
  569. if (wnd.Top < screen.Top)
  570. {
  571. double diff = Math.Abs(screen.Top - wnd.Top);
  572. window.Top = wnd.Top + diff;
  573. }
  574. if (wnd.Bottom > screen.Bottom)
  575. {
  576. double diff = wnd.Bottom - screen.Bottom;
  577. window.Top = wnd.Top - diff;
  578. }
  579. if (wnd.Left < screen.Left)
  580. {
  581. double diff = Math.Abs(screen.Left - wnd.Left);
  582. window.Left = wnd.Left + diff;
  583. }
  584. if (wnd.Right > screen.Right)
  585. {
  586. double diff = wnd.Right - screen.Right;
  587. window.Left = wnd.Left - diff;
  588. }
  589. }
  590. }
  591. #endregion
  592. #region W32 API
  593. [DllImport("user32.dll", SetLastError = true)]
  594. [return: MarshalAs(UnmanagedType.Bool)]
  595. private static extern bool GetCursorPos(ref W32Point pt);
  596. [DllImport("user32.dll", SetLastError = true)]
  597. [return: MarshalAs(UnmanagedType.Bool)]
  598. private static extern bool GetMonitorInfo(IntPtr hMonitor, ref W32MonitorInfo lpmi);
  599. [DllImport("user32.dll")]
  600. private static extern IntPtr MonitorFromPoint(W32Point pt, uint dwFlags);
  601. [StructLayout(LayoutKind.Sequential)]
  602. public struct W32Point
  603. {
  604. public int X;
  605. public int Y;
  606. }
  607. [StructLayout(LayoutKind.Sequential)]
  608. internal struct W32MonitorInfo
  609. {
  610. public int Size;
  611. public W32Rect Monitor;
  612. public W32Rect WorkArea;
  613. public uint Flags;
  614. }
  615. [StructLayout(LayoutKind.Sequential)]
  616. internal struct W32Rect
  617. {
  618. public int Left;
  619. public int Top;
  620. public int Right;
  621. public int Bottom;
  622. }
  623. #endregion
  624. }
  625. }