using System.Runtime.InteropServices; using System.Windows.Forms.VisualStyles; using System.Windows.Input; namespace Caliburn.Micro { using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Linq; using System.Windows.Navigation; using Caliburn.Micro.Core; using System.Windows.Media; /// /// A service that manages windows. /// public interface IWindowManager { /// /// Shows a modal dialog for the specified model. /// /// The root model. /// The context. /// The optional dialog settings. /// The dialog result. bool? ShowDialog(object rootModel, object context = null, IDictionary settings = null); /// /// Shows a non-modal window for the specified model. /// /// The root model. /// The context. /// The optional window settings. void ShowWindow(object rootModel, object context = null, IDictionary settings = null); /// /// Shows a popup at the current mouse position. /// /// The root model. /// The view context. /// The optional popup settings. void ShowPopup(object rootModel, object context = null, IDictionary settings = null); } /// /// A service that manages windows. /// public class WindowManager : IWindowManager { /// /// Shows a modal dialog for the specified model. /// /// The root model. /// The context. /// The dialog popup settings. /// The dialog result. public virtual bool? ShowDialog(object rootModel, object context = null, IDictionary settings = null){ Window window = CreateWindow(rootModel, true, context, settings); window.ShowInTaskbar = false; window.ResizeMode = ResizeMode.NoResize; window.WindowStyle = WindowStyle.SingleBorderWindow; return window.ShowDialog(); } public virtual bool? ShowDialog(object rootModel, Point position, object context = null, IDictionary settings = null) { Window window = CreateWindow(rootModel, true, context, settings); window.ShowInTaskbar = false; window.ResizeMode = ResizeMode.NoResize; window.WindowStyle = WindowStyle.SingleBorderWindow; window.WindowStartupLocation = WindowStartupLocation.Manual; window.Owner = Application.Current.MainWindow; window.Left = position.X; window.Top = position.Y; return window.ShowDialog(); } public virtual bool? ShowDialog(object rootModel, Window owner, object context = null, IDictionary settings = null) { Window window = CreateWindow(rootModel, true, context, settings); window.ShowInTaskbar = false; window.ResizeMode = ResizeMode.NoResize; window.WindowStyle = WindowStyle.SingleBorderWindow; window.Owner = owner; return window.ShowDialog(); } public virtual bool? ShowDialog(object rootModel, WindowState pWindowState , object context = null, IDictionary settings = null) { Window window = CreateWindow(rootModel, true, context, settings); window.ShowInTaskbar = false; window.ResizeMode = ResizeMode.CanResize; window.WindowStyle = WindowStyle.SingleBorderWindow; window.WindowState = pWindowState; return window.ShowDialog(); } public virtual bool? ShowDialogWithNoWindow(object rootModel, object context = null, IDictionary settings = null) { Window window = CreateWindow(rootModel, true, context, settings); window.ShowInTaskbar = false; window.ResizeMode = ResizeMode.NoResize; window.WindowStyle = WindowStyle.None; return window.ShowDialog(); } /// /// Shows a special modal dialog for the specified model. /// /// The root model. /// The context. /// The dialog result. public virtual bool? ShowDialogWithNoStyle(object rootModel, object context = null) { Window window = CreateWindow(rootModel, true, context); window.WindowStyle = WindowStyle.None; window.AllowsTransparency = true; window.ResizeMode = ResizeMode.NoResize; window.Background = new SolidColorBrush(Colors.Transparent); window.ShowInTaskbar = false; window.WindowStartupLocation = WindowStartupLocation.CenterScreen; window.Topmost = true; return window.ShowDialog(); } /// /// Creates a window. /// /// The view model. /// Whethor or not the window is being shown as a dialog. /// The view context. /// Call back and return the new window when create window completed /// The window. protected virtual Window CreateWindow(object rootModel, bool isDialog, object context, Action createCompletedCallBack = null) { var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context), isDialog); ViewModelBinder.Bind(rootModel, view, context); var haveDisplayName = rootModel as IHaveDisplayName; if (haveDisplayName != null && !ConventionManager.HasBinding(view, Window.TitleProperty)) { var binding = new Binding("DisplayName") { Mode = BindingMode.OneWay }; view.SetBinding(Window.TitleProperty, binding); } if (createCompletedCallBack != null) { createCompletedCallBack(view); } new WindowConductor(rootModel, view); return view; } /// /// Shows a window for the specified model. /// /// The root model. /// The context. /// The optional window settings. public virtual void ShowWindow(object rootModel, object context = null, IDictionary settings = null){ NavigationWindow navWindow = null; var application = Application.Current; if (application != null && application.MainWindow != null) { navWindow = application.MainWindow as NavigationWindow; } if(navWindow != null) { var window = CreatePage(rootModel, context, settings); navWindow.Navigate(window); } else { CreateWindow(rootModel, false, context, settings).Show(); } } /// /// Shows a popup at the current mouse position. /// /// The root model. /// The view context. /// The optional popup settings. public virtual void ShowPopup(object rootModel, object context = null, IDictionary settings = null) { var popup = CreatePopup(rootModel, settings); var view = ViewLocator.LocateForModel(rootModel, popup, context); popup.Child = view; popup.SetValue(View.IsGeneratedProperty, true); ViewModelBinder.Bind(rootModel, popup, null); Action.SetTargetWithoutContext(view, rootModel); var activatable = rootModel as IActivate; if (activatable != null) { activatable.Activate(); } var deactivator = rootModel as IDeactivate; if (deactivator != null) { popup.Closed += delegate { deactivator.Deactivate(true); }; } popup.IsOpen = true; popup.CaptureMouse(); } /// /// Creates a popup for hosting a popup window. /// /// The model. /// The optional popup settings. /// The popup. protected virtual Popup CreatePopup(object rootModel, IDictionary settings) { var popup = new Popup(); if (ApplySettings(popup, settings)) { if (!settings.ContainsKey("PlacementTarget") && !settings.ContainsKey("Placement")) popup.Placement = PlacementMode.MousePoint; if (!settings.ContainsKey("AllowsTransparency")) popup.AllowsTransparency = true; }else { popup.AllowsTransparency = true; popup.Placement = PlacementMode.MousePoint; } return popup; } /// /// Creates a window. /// /// The view model. /// Whethor or not the window is being shown as a dialog. /// The view context. /// The optional popup settings. /// The window. protected virtual Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary settings) { var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context), isDialog); ViewModelBinder.Bind(rootModel, view, context); var haveDisplayName = rootModel as IHaveDisplayName; if (haveDisplayName != null && !ConventionManager.HasBinding(view, Window.TitleProperty)) { var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay }; view.SetBinding(Window.TitleProperty, binding); } ApplySettings(view, settings); new WindowConductor(rootModel, view); return view; } /// /// Makes sure the view is a window is is wrapped by one. /// /// The view model. /// The view. /// Whethor or not the window is being shown as a dialog. /// The window. protected virtual Window EnsureWindow(object model, object view, bool isDialog) { var window = view as Window; if (window == null) { window = new Window { Content = view, SizeToContent = SizeToContent.WidthAndHeight }; window.SetValue(View.IsGeneratedProperty, true); var owner = InferOwnerOf(window); if (owner != null) { window.WindowStartupLocation = WindowStartupLocation.CenterOwner; window.Owner = owner; } else { window.WindowStartupLocation = WindowStartupLocation.CenterScreen; } } else { var owner = InferOwnerOf(window); if (owner != null && isDialog) { window.Owner = owner; } } return window; } /// /// Infers the owner of the window. /// /// The window to whose owner needs to be determined. /// The owner. protected virtual Window InferOwnerOf(Window window) { var application = Application.Current; if (application == null) { return null; } var active = application.Windows.OfType().FirstOrDefault(x => x.IsActive); active = active ?? (PresentationSource.FromVisual(application.MainWindow) == null ? null : application.MainWindow); return active == window ? null : active; } /// /// Creates the page. /// /// The root model. /// The context. /// The optional popup settings. /// The page. public virtual Page CreatePage(object rootModel, object context, IDictionary settings) { var view = EnsurePage(rootModel, ViewLocator.LocateForModel(rootModel, null, context)); ViewModelBinder.Bind(rootModel, view, context); var haveDisplayName = rootModel as IHaveDisplayName; if (haveDisplayName != null && !ConventionManager.HasBinding(view, Page.TitleProperty)) { var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay }; view.SetBinding(Page.TitleProperty, binding); } ApplySettings(view, settings); var activatable = rootModel as IActivate; if (activatable != null) { activatable.Activate(); } var deactivatable = rootModel as IDeactivate; if (deactivatable != null) { view.Unloaded += (s, e) => deactivatable.Deactivate(true); } return view; } /// /// Ensures the view is a page or provides one. /// /// The model. /// The view. /// The page. protected virtual Page EnsurePage(object model, object view) { var page = view as Page; if(page == null) { page = new Page { Content = view }; page.SetValue(View.IsGeneratedProperty, true); } return page; } bool ApplySettings(object target, IEnumerable> settings) { if (settings != null) { var type = target.GetType(); foreach (var pair in settings) { var propertyInfo = type.GetProperty(pair.Key); if (propertyInfo != null) { propertyInfo.SetValue(target, pair.Value, null); } } return true; } return false; } class WindowConductor { bool deactivatingFromView; bool deactivateFromViewModel; bool actuallyClosing; readonly Window view; readonly object model; public WindowConductor(object model, Window view) { this.model = model; this.view = view; var activatable = model as IActivate; if (activatable != null) { activatable.Activate(); } var deactivatable = model as IDeactivate; if (deactivatable != null) { view.Closed += Closed; deactivatable.Deactivated += Deactivated; } var guard = model as IGuardClose; if (guard != null) { view.Closing += Closing; } } void Closed(object sender, EventArgs e) { view.Closed -= Closed; view.Closing -= Closing; if (deactivateFromViewModel) { return; } var deactivatable = (IDeactivate)model; deactivatingFromView = true; deactivatable.Deactivate(true); deactivatingFromView = false; } void Deactivated(object sender, DeactivationEventArgs e) { if (!e.WasClosed) { return; } ((IDeactivate)model).Deactivated -= Deactivated; if (deactivatingFromView) { return; } deactivateFromViewModel = true; actuallyClosing = true; view.Close(); actuallyClosing = false; deactivateFromViewModel = false; } void Closing(object sender, CancelEventArgs e) { if (e.Cancel) { return; } var guard = (IGuardClose)model; if (actuallyClosing) { actuallyClosing = false; return; } bool runningAsync = false, shouldEnd = false; guard.CanClose(canClose => { Execute.OnUIThread(() => { if(runningAsync && canClose) { actuallyClosing = true; view.Close(); } else { e.Cancel = !canClose; } shouldEnd = true; }); }); if (shouldEnd) { return; } runningAsync = e.Cancel = true; } } } public static class WindowExtensions { #region Public Methods public static bool ActivateCenteredToMouse(this Window window) { ComputeTopLeft(ref window); return window.Activate(); } public static void ShowCenteredToMouse(this Window window) { // in case the default start-up location isn't set to Manual WindowStartupLocation oldLocation = window.WindowStartupLocation; // set location to manual -> window will be placed by Top and Left property window.WindowStartupLocation = WindowStartupLocation.Manual; ComputeTopLeft(ref window); window.ShowDialog(); window.WindowStartupLocation = oldLocation; } #endregion #region Methods private static void ComputeTopLeft(ref Window window) { W32Point pt = new W32Point(); if (!GetCursorPos(ref pt)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // 0x00000002: return nearest monitor if pt is not contained in any monitor. IntPtr monHandle = MonitorFromPoint(pt, 0x00000002); W32MonitorInfo monInfo = new W32MonitorInfo(); monInfo.Size = Marshal.SizeOf(typeof(W32MonitorInfo)); if (!GetMonitorInfo(monHandle, ref monInfo)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // use WorkArea struct to include the taskbar position. W32Rect monitor = monInfo.WorkArea; double offsetX = Math.Round(window.Width / 2); double offsetY = Math.Round(window.Height / 2); double top = pt.Y - offsetY; double left = pt.X - offsetX; Rect screen = new Rect( new Point(monitor.Left, monitor.Top), new Point(monitor.Right, monitor.Bottom)); Rect wnd = new Rect( new Point(left, top), new Point(left + window.Width, top + window.Height)); window.Top = wnd.Top; window.Left = wnd.Left; if (!screen.Contains(wnd)) { if (wnd.Top < screen.Top) { double diff = Math.Abs(screen.Top - wnd.Top); window.Top = wnd.Top + diff; } if (wnd.Bottom > screen.Bottom) { double diff = wnd.Bottom - screen.Bottom; window.Top = wnd.Top - diff; } if (wnd.Left < screen.Left) { double diff = Math.Abs(screen.Left - wnd.Left); window.Left = wnd.Left + diff; } if (wnd.Right > screen.Right) { double diff = wnd.Right - screen.Right; window.Left = wnd.Left - diff; } } } #endregion #region W32 API [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetCursorPos(ref W32Point pt); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref W32MonitorInfo lpmi); [DllImport("user32.dll")] private static extern IntPtr MonitorFromPoint(W32Point pt, uint dwFlags); [StructLayout(LayoutKind.Sequential)] public struct W32Point { public int X; public int Y; } [StructLayout(LayoutKind.Sequential)] internal struct W32MonitorInfo { public int Size; public W32Rect Monitor; public W32Rect WorkArea; public uint Flags; } [StructLayout(LayoutKind.Sequential)] internal struct W32Rect { public int Left; public int Top; public int Right; public int Bottom; } #endregion } }