WindowManager.cs 29 KB

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