/// <summary> /// Handle DPI changes for the specified ContextMenuStrip /// </summary> /// <param name="contextMenuStrip">ContextMenuStrip</param> /// <returns>DpiHandler</returns> public static DpiHandler AttachDpiHandler(this ContextMenuStrip contextMenuStrip) { // Create a DpiHandler which runs "outside" of the contextMenu (not via WinProc) var dpiHandler = new DpiHandler(true); dpiHandler.MessageHandler = contextMenuStrip.WinProcFormsMessages().Subscribe(message => dpiHandler.HandleContextMenuMessages(message)); return(dpiHandler); }
/// <summary> /// Handle DPI changes for the specified Form /// Using this DOES NOT enable dpi scaling in the non client area, for this you will need to call: /// DpiHandler.TryEnableNonClientDpiScaling(this.Handle) from the WndProc in the WM_NCCREATE message. /// It's better to extend DpiAwareForm, which does this for you. /// </summary> /// <param name="form">Control</param> /// <returns>DpiHandler</returns> public static DpiHandler AttachDpiHandler(this Form form) { // Create a DpiHandler which runs "outside" of the form (not via WinProc) var dpiHandler = new DpiHandler(true); dpiHandler.MessageHandler = form.WinProcFormsMessages().Subscribe(message => dpiHandler.HandleWindowMessages(message)); return(dpiHandler); }
/// <summary> /// Handle DPI changes for the specified Form /// Using this DOES NOT enable dpi scaling in the non client area, for this you will need to call: /// DpiHandler.TryEnableNonClientDpiScaling(this.Handle) from the WndProc in the WM_NCCREATE message. /// It's better to extend DpiAwareForm, which does this for you. /// </summary> /// <param name="form">Control</param> /// <returns>DpiHandler</returns> public static DpiHandler AttachDpiHandler(this Form form) { // Create a DpiHandler which runs "outside" of the form (not via WinProc) var dpiHandler = new DpiHandler(true); var listener = new WinProcListener(form); listener.AddHook(dpiHandler.HandleWindowMessages); dpiHandler.MessageHandler = listener; return(dpiHandler); }
/// <summary> /// Handle DPI changes for the specified ContextMenuStrip /// </summary> /// <param name="contextMenuStrip">ContextMenuStrip</param> /// <returns>DpiHandler</returns> public static DpiHandler AttachDpiHandler(this ContextMenuStrip contextMenuStrip) { // Create a DpiHandler which runs "outside" of the contextMenu (not via WinProc) var dpiHandler = new DpiHandler(true); var listener = new WinProcListener(contextMenuStrip); listener.AddHook(dpiHandler.HandleContextMenuMessages); dpiHandler.MessageHandler = listener; return(dpiHandler); }
/// <summary> /// Setup the Bitmap scaling (for icons) /// </summary> private void SetupBitmapScaleHandler() { ContextMenuDpiHandler = contextMenu.AttachDpiHandler(); var dpiChangeSubscription = DpiHandler.OnDpiChangeInfo.Subscribe(info => { switch (info.DpiChangeEventType) { case DpiChangeEventTypes.Before: // Change the ImageScalingSize before setting the bitmaps var width = DpiHandler.ScaleWithDpi(coreConfiguration.IconSize.Width, info.NewDpi); var size = new Size(width, width); contextMenu.SuspendLayout(); contextMenu.ImageScalingSize = size; contextmenu_quicksettings.Size = new Size(170, width + 8); break; case DpiChangeEventTypes.After: // Redraw the form contextMenu.ResumeLayout(true); contextMenu.Refresh(); notifyIcon.Icon = GreenshotResources.GetGreenshotIcon(); break; } }); var contextMenuResourceScaleHandler = BitmapScaleHandler.WithComponentResourceManager(ContextMenuDpiHandler, GetType(), (bitmap, dpi) => bitmap.ScaleIconForDisplaying(dpi)); contextMenuResourceScaleHandler.AddTarget(contextmenu_capturewindow, "contextmenu_capturewindow.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_capturearea, "contextmenu_capturearea.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_capturelastregion, "contextmenu_capturelastregion.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_capturefullscreen, "contextmenu_capturefullscreen.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_captureclipboard, "contextmenu_captureclipboard.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_openfile, "contextmenu_openfile.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_settings, "contextmenu_settings.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_help, "contextmenu_help.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_donate, "contextmenu_donate.Image"); contextMenuResourceScaleHandler.AddTarget(contextmenu_exit, "contextmenu_exit.Image"); // this is special handling, for the icons which come from the executables var exeBitmapScaleHandler = BitmapScaleHandler.Create <string>(ContextMenuDpiHandler, (path, dpi) => PluginUtils.GetCachedExeIcon(path, 0, dpi >= 120), (bitmap, dpi) => bitmap.ScaleIconForDisplaying(dpi)); exeBitmapScaleHandler.AddTarget(contextmenu_captureie, PluginUtils.GetExePath("iexplore.exe")); // Add cleanup Application.ApplicationExit += (sender, args) => { dpiChangeSubscription.Dispose(); ContextMenuDpiHandler.Dispose(); contextMenuResourceScaleHandler.Dispose(); exeBitmapScaleHandler.Dispose(); }; }
/// <summary> /// Implementation of the IGreenshotPlugin.Initialize /// </summary> /// <returns>true if plugin is initialized, false if not (doesn't show)</returns> public bool Initialize() { _greenshotHost.ContextMenuDpiHandler.OnDpiChanged.Subscribe(dpi => { var size = DpiHandler.ScaleWithDpi(_coreConfiguration.IconSize.Width, dpi); _jiraConnector.UpdateSvgSize(size); }); return(true); }
/// <summary> /// </summary> /// <param name="bitmap"></param> /// <param name="dpi"></param> /// <returns></returns> private Bitmap ScaleIconForDisplaying(Bitmap bitmap, double dpi) { var newSize = DpiHandler.ScaleWithDpi(16, dpi); var result = new Bitmap(newSize, newSize, bitmap.PixelFormat); using (var graphics = Graphics.FromImage(result)) { graphics.InterpolationMode = InterpolationMode.NearestNeighbor; graphics.DrawImage(bitmap, new Rectangle(0, 0, newSize, newSize), new Rectangle(0, 0, 16, 16), GraphicsUnit.Pixel); } return(result); }
/// <summary> /// Handle DPI changes for the specified Window, this is actually not really needed for WPF. /// </summary> /// <param name="window">Window</param> /// <returns>DpiHandler</returns> public static DpiHandler AttachDpiHandler(this Window window) { if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Creating a dpi handler for {0}", window.GetType()); } var dpiHandler = new DpiHandler(); AttachDpiHandler(window, dpiHandler); return(dpiHandler); }
public void Test_UnscaleWithDpi() { var size96 = DpiHandler.UnscaleWithDpi(16, 96); Assert.Equal(16, size96); var size120 = DpiHandler.UnscaleWithDpi(16, 120); Assert.Equal(12, size120); var size144 = DpiHandler.UnscaleWithDpi(16, 144); Assert.Equal(10, size144); var size192 = DpiHandler.UnscaleWithDpi(16, 192); Assert.Equal(8, size192); }
public void Test_ScaleWithDpi() { var size96 = DpiHandler.ScaleWithDpi(16, 96); Assert.Equal(16, size96); var size120 = DpiHandler.ScaleWithDpi(16, 120); Assert.Equal(20, size120); var size144 = DpiHandler.ScaleWithDpi(16, 144); Assert.Equal(24, size144); var size192 = DpiHandler.ScaleWithDpi(16, 192); Assert.Equal(32, size192); }
public void TestScreenbounds() { var screenboundsAllScreens = GetScreenBoundsAllScreens(); var screenboundsDisplayInfo = DisplayInfo.ScreenBounds; // The following scales the screenboundsAllScreens which comes from build in code without DPI awareness // with the current DPI so it should also work when running with a different DPI setting var monitorHandle = DisplayInfo.AllDisplayInfos.First().MonitorHandle; NativeDpiMethods.GetDpiForMonitor(monitorHandle, Dpi.Enums.MonitorDpiType.EffectiveDpi, out var xDpi, out var yDpi); if (xDpi != DpiHandler.DefaultScreenDpi) { var newSize = DpiHandler.ScaleWithDpi(screenboundsAllScreens.Size, xDpi); screenboundsAllScreens = new NativeRect(screenboundsAllScreens.Location, newSize); } Assert.Equal(screenboundsAllScreens, screenboundsDisplayInfo); }
public void Test_ScaleWithDpi_UnscaleWithDpi() { var testSize = new NativeSize(16, 16); var size96 = DpiHandler.ScaleWithDpi(testSize, 96); var resultSize96 = DpiHandler.UnscaleWithDpi(size96, 96); Assert.Equal(testSize, resultSize96); var size120 = DpiHandler.ScaleWithDpi(testSize, 120); var resultSize120 = DpiHandler.UnscaleWithDpi(size120, 120); Assert.Equal(testSize, resultSize120); var size144 = DpiHandler.ScaleWithDpi(testSize, 144); var resultSize144 = DpiHandler.UnscaleWithDpi(size144, 144); Assert.Equal(testSize, resultSize144); }
/// <summary> /// Prepare an "icon" to be displayed correctly scaled /// </summary> /// <param name="original">original icon Bitmap</param> /// <param name="dpi">double with the dpi value</param> /// <param name="baseSize">the base size of the icon, default is 16</param> /// <returns>Bitmap</returns> public static Bitmap ScaleIconForDisplaying(this Bitmap original, uint dpi, int baseSize = 16) { if (original == null) { return(null); } if (dpi < DpiHandler.DefaultScreenDpi) { dpi = DpiHandler.DefaultScreenDpi; } var width = DpiHandler.ScaleWithDpi(baseSize, dpi); if (original.Width == width) { return(original); } return(original.Resize(true, width, width, interpolationMode: InterpolationMode.NearestNeighbor)); }
/// <summary> /// This takes the height and width of the Popup and will return the location where it should be displayed /// </summary> /// <param name="actualPopupWidth">double</param> /// <param name="actualPopupHeight">double</param> /// <returns>Point</returns> public Point GetPosition(double actualPopupWidth, double actualPopupHeight) { var taskbar = Shell32Api.TaskbarPosition; var taskbarBounds = taskbar.Bounds; var actualSize = new NativeSize((int)actualPopupWidth, (int)actualPopupHeight); // Use the DPI of the desktop var dpi = NativeDpiMethods.GetDpi(InteropWindowQuery.GetDesktopWindow().Handle); actualSize = DpiHandler.ScaleWithDpi(actualSize, dpi); int x, y; // Define the new position switch (taskbar.AppBarEdge) { case AppBarEdges.Left: x = taskbarBounds.Right + _xOffset; y = taskbarBounds.Bottom - _yOffset - actualSize.Height; break; case AppBarEdges.Top: x = taskbarBounds.Right - _xOffset - actualSize.Width; y = taskbarBounds.Bottom + _yOffset; break; case AppBarEdges.Right: x = taskbarBounds.Left - _xOffset - actualSize.Width; y = taskbarBounds.Bottom - _yOffset - actualSize.Height; break; case AppBarEdges.Bottom: default: x = taskbarBounds.Right - _xOffset - actualSize.Width; y = taskbarBounds.Top - _yOffset - actualSize.Height; break; } var position = DpiHandler.UnscaleWithDpi(new NativePoint(x, y), dpi); Log.Debug().WriteLine("Taskbar location {0} at {1}, calculate popup position: {2}", taskbar.AppBarEdge, taskbarBounds, position); return(position); }
public FormExtendsDpiAwareForm() { InitializeComponent(); _contextMenuDpiHandler = contextMenuStrip1.AttachDpiHandler(); _contextMenuDpiHandler.OnDpiChanged.Subscribe(dpi => { Log.Info().WriteLine("ContextMenuStrip DPI: {0}", dpi); }); ScaleHandler = BitmapScaleHandler.WithComponentResourceManager(DpiHandler, GetType(), ScaleIconForDisplaying); ScaleHandler.AddTarget(somethingMenuItem, "somethingMenuItem.Image"); ScaleHandler.AddTarget(something2MenuItem, "something2MenuItem.Image"); EnvironmentMonitor.EnvironmentUpdateEvents.Subscribe(args => { Log.Info().WriteLine("{0} - {1}", args.SystemParametersInfoAction, args.Area); MessageBox.Show(this, $"{args.SystemParametersInfoAction} - {args.Area}", "Change!"); }); }
public FormWithAttachedDpiHandler() { _dpiHandler = this.AttachDpiHandler(); InitializeComponent(); ScaleHandler = BitmapScaleHandler.WithComponentResourceManager(_dpiHandler, GetType(), ScaleIconForDisplaying); // This can be used to do something with DPI changes, subscription should be disposed! _dpiChangeSubscription = _dpiHandler.OnDpiChanged.Subscribe(dpi => { var width = DpiHandler.ScaleWithDpi(20, dpi); var size = new Size(width, width); //menuStrip1.ImageScalingSize = size; }); ScaleHandler.AddTarget(somethingMenuItem, "somethingMenuItem.Image"); ScaleHandler.AddTarget(something2MenuItem, "something2MenuItem.Image"); EnvironmentMonitor.EnvironmentUpdateEvents.Subscribe(args => { Log.Info().WriteLine("{0} - {1}", args.SystemParametersInfoAction, args.Area); MessageBox.Show(this, $"{args.SystemParametersInfoAction} - {args.Area}", "Change!"); }); }
/// <summary> /// Attach a DpiHandler to the specified window /// </summary> /// <param name="window">Windows</param> /// <param name="dpiHandler">DpiHandler</param> private static void AttachDpiHandler(Window window, DpiHandler dpiHandler) { var windowInteropHelper = new WindowInteropHelper(window); // Due to the nature of the WPF Window, we cannot get to the WM_NCCREATE event from outside // The SourceInitialized event is actually called when this event happens. window.SourceInitialized += (sender, args) => { DpiHandler.TryEnableNonClientDpiScaling(windowInteropHelper.Handle); // Get the HwndSource for the window, this works very early in the process by calling EnsureHandle (which creates the handle) var hwndSource = HwndSource.FromHwnd(windowInteropHelper.Handle); if (hwndSource == null) { throw new NotSupportedException("No HwndSource available, although EnsureHandle was called?"); } hwndSource.AddHook(dpiHandler.HandleWindowMessages); dpiHandler.MessageHandler = hwndSource; // Add the layout transform action dpiHandler.OnDpiChanged.Subscribe(dpi => window.UpdateLayoutTransform(dpi / DpiHandler.DefaultScreenDpi)); // Apply scaling window.UpdateLayoutTransform(DpiHandler.GetDpi(windowInteropHelper.Handle) / DpiHandler.DefaultScreenDpi); }; }
protected AbstractAdorner(IDrawableContainer owner) { _size.Width = _size.Height = DpiHandler.ScaleWithDpi(5, NativeDpiMethods.GetDpi(InteropWindowQuery.GetDesktopWindow().Handle)); Owner = owner; }
public static int GetDpi(Window window) { return(DpiHandler.GetDpi(new WindowInteropHelper(window).Handle)); }
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); DpiHandler.EnableDpiAwareness(); }
/// <summary> /// This method will create and show the destination picker menu /// </summary> /// <param name="addDynamics">Boolean if the dynamic values also need to be added</param> /// <param name="surface">The surface which can be exported</param> /// <param name="captureDetails">Details for the surface</param> /// <param name="destinations">The list of destinations to show</param> /// <returns></returns> protected ExportInformation ShowPickerMenu(bool addDynamics, ISurface surface, ICaptureDetails captureDetails, IEnumerable <IDestination> destinations) { var menu = new ContextMenuStrip { Tag = null, TopLevel = true }; var dpiHandler = menu.AttachDpiHandler(); var bitmapScaleHandler = BitmapScaleHandler.Create <IDestination>( dpiHandler, (destination, dpi) => destination.GetDisplayIcon(dpi), (bitmap, d) => bitmap.ScaleIconForDisplaying(d)); dpiHandler.OnDpiChanged.Subscribe(dpi => { var width = DpiHandler.ScaleWithDpi(CoreConfiguration.IconSize.Width, dpi); var size = new Size(width, width); menu.ImageScalingSize = size; }); // Generate an empty ExportInformation object, for when nothing was selected. var exportInformation = new ExportInformation("", GreenshotLanguage.SettingsDestinationPicker); menu.Closing += (source, eventArgs) => { Log.Debug().WriteLine("Close reason: {0}", eventArgs.CloseReason); switch (eventArgs.CloseReason) { case ToolStripDropDownCloseReason.AppFocusChange: if (menu.Tag == null) { // Do not allow the close if the tag is not set, this means the user clicked somewhere else. eventArgs.Cancel = true; } else { Log.Debug().WriteLine("Letting the menu 'close' as the tag is set to '{0}'", menu.Tag); } break; case ToolStripDropDownCloseReason.ItemClicked: case ToolStripDropDownCloseReason.CloseCalled: // The ContextMenuStrip can be "closed" for these reasons. break; case ToolStripDropDownCloseReason.Keyboard: // Dispose as the close is clicked if (!captureDetails.HasDestination("Editor")) { surface.Dispose(); surface = null; } break; default: eventArgs.Cancel = true; break; } }; menu.MouseEnter += (sender, args) => { // in case the menu has been unfocused, focus again so that dropdown menus will still open on mouseenter if (!menu.ContainsFocus) { menu.Focus(); } }; foreach (var destination in destinations) { // Fix foreach loop variable for the delegate var item = destination.GetMenuItem(addDynamics, menu, async(sender, e) => { var toolStripMenuItem = sender as ToolStripMenuItem; var clickedDestination = (IDestination)toolStripMenuItem?.Tag; if (clickedDestination == null) { return; } menu.Tag = clickedDestination.Designation; // Export exportInformation = await clickedDestination.ExportCaptureAsync(true, surface, captureDetails); if (exportInformation != null && exportInformation.ExportMade) { Log.Info().WriteLine("Export to {0} success, closing menu", exportInformation.DestinationDescription); // close menu menu.Close(); menu.Dispose(); // Cleanup surface, only if there is no editor in the destinations and we didn't export to the editor if (!captureDetails.HasDestination("Editor") && !"Editor".Equals(clickedDestination.Designation)) { surface.Dispose(); surface = null; } } else { Log.Info().WriteLine("Export cancelled or failed, showing menu again"); // Make sure a click besides the menu don't close it. menu.Tag = null; // This prevents the problem that the context menu shows in the task-bar ShowMenuAtCursor(menu); } }, bitmapScaleHandler ); if (item != null) { menu.Items.Add(item); } } // Close menu.Items.Add(new ToolStripSeparator()); var closeItem = new ToolStripMenuItem(GreenshotLanguage.Close) { Image = GreenshotResources.GetBitmap("Close.Image") }; closeItem.Click += (sender, args) => { // This menu entry is the close itself, we can dispose the surface menu.Close(); menu.Dispose(); // Only dispose if there is a destination which keeps the capture if (captureDetails.HasDestination("Editor")) { return; } surface.Dispose(); surface = null; }; menu.Items.Add(closeItem); ShowMenuAtCursor(menu); return(exportInformation); }