I want to be able to perform lens magnificaiton on top of the windows taskbar. So far I've been unsuccessful in implementing this seeing as the taskbar will always open on top of my window. Windows built-in magnifier is able to do this so I'm hoping it is indeed possible.
I've attached two screenshots showing Windows built-in magnifier and how it is able to magnify the taskbar and how my application will render below the taskbar.
Windows built-in Magnifier:
My application:
Is there any way to have my application render above the taskbar and thus magnify the taskbar?
<Window x:Class="WpfNativeTesting.MagnificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfNativeTesting"
mc:Ignorable="d"
Title="MagnificationWindow"
Height="400"
Width="400"
WindowStyle="None"
ResizeMode="NoResize"
AllowsTransparency="true"
ShowInTaskbar="False"
Topmost="True">
<Grid x:Name="ContainerGrid">
<Grid x:Name="MagnificationGrid" />
</Grid>
</Window>
public partial class MagnificationWindow : Window
{
private IntPtr HWnd;
private IntPtr HWndMag;
private bool MagnificationInitialized = false;
private DispatcherTimer Timer = new DispatcherTimer();
private RECT MagWindowRect = new RECT();
private bool IsColorEffectSet = false;
private float magnification = 1.0f;
public float Magnification
{
get { return magnification; }
set
{
if (value < 1.0f)
{
value = 1.0f;
}
if (HWndMag != null)
{
if (magnification != value)
{
magnification = value;
Transformation matrix = new Transformation(magnification);
NativeMethods.MagSetWindowTransform(HWndMag, ref matrix);
}
}
}
}
public MagnificationWindow()
{
InitializeComponent();
Loaded += MagnificationWindow_Loaded;
Show();
}
private void MagnificationWindow_Loaded(object sender, RoutedEventArgs e)
{
HWnd = new WindowInteropHelper(this).Handle;
var exStyle = NativeMethods.GetWindowLong(HWnd, NativeMethods.GWL_EXSTYLE);
exStyle |= (int)ExtendedWindowStyles.WS_EX_TOPMOST | (int)ExtendedWindowStyles.WS_EX_LAYERED | (int)ExtendedWindowStyles.WS_EX_TRANSPARENT;
NativeMethods.SetWindowLong(HWnd, NativeMethods.GWL_EXSTYLE, exStyle);
var style = NativeMethods.GetWindowLong(HWnd, NativeMethods.GWL_STYLE);
style |= (int)WindowStyles.WS_CAPTION | (int)WindowStyles.WS_SYSMENU;
NativeMethods.SetWindowLong(HWnd, NativeMethods.GWL_STYLE, exStyle);
MagnificationInitialized = NativeMethods.MagInitialize();
if (MagnificationInitialized)
{
SetupMagnifier();
Timer.Interval = TimeSpan.FromMilliseconds(NativeMethods.USER_TIMER_MINIMUM);
Timer.Tick += Timer_Tick;
Timer.Start();
}
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
RemoveMagnifier();
}
private void Timer_Tick(object sender, EventArgs e)
{
UpdateMaginifier();
}
private void SetupMagnifier()
{
var hInst = NativeMethods.GetModuleHandle(null);
NativeMethods.GetClientRect(HWnd, ref MagWindowRect);
HWndMag = NativeMethods.CreateWindow((int)ExtendedWindowStyles.WS_EX_STATICEDGE, NativeMethods.WC_MAGNIFIER,
"MagnificationWindow", (int)WindowStyles.WS_CHILD | (int)MagnifierStyle.MS_SHOWMAGNIFIEDCURSOR | (int)WindowStyles.WS_VISIBLE,
MagWindowRect.left, MagWindowRect.top, MagWindowRect.right, MagWindowRect.bottom, HWnd, IntPtr.Zero, hInst, IntPtr.Zero);
NativeMethods.MagShowSystemCursor(false);
if (HWndMag == IntPtr.Zero)
{
return;
}
var matrix = new Transformation(Magnification);
NativeMethods.MagSetWindowTransform(HWndMag, ref matrix);
}
private void UpdateMaginifier()
{
if (!MagnificationInitialized || HWndMag == IntPtr.Zero)
{
return;
}
POINT mousePoint = new POINT();
RECT sourceRect = new RECT();
NativeMethods.GetCursorPos(ref mousePoint);
int width = (int)((MagWindowRect.right - MagWindowRect.left) / Magnification);
int height = (int)((MagWindowRect.bottom - MagWindowRect.top) / Magnification);
sourceRect.left = mousePoint.x - width / 2;
sourceRect.top = mousePoint.y - height / 2;
NativeMethods.MagSetWindowSource(HWndMag, sourceRect);
POINT mouse = new POINT();
NativeMethods.GetCursorPos(ref mouse);
NativeMethods.SetWindowPos(HWnd, NativeMethods.HWND_TOPMOST, mouse.x - (int)(magnification * width / 2), mouse.y - (int)(magnification * height / 2), width, height,
(int)SetWindowPosFlags.SWP_NOACTIVATE |
(int)SetWindowPosFlags.SWP_NOSIZE);
NativeMethods.InvalidateRect(HWndMag, IntPtr.Zero, true);
}
public void RemoveMagnifier()
{
if (MagnificationInitialized)
{
NativeMethods.MagUninitialize();
MagnificationInitialized = false;
}
}
// ...
}
I posted this question in the Microsoft Q/A forum and got a solution that got it working.
https://learn.microsoft.com/en-us/answers/questions/54196/magnifier-control-unable-to-magnify-the-taskbar-st.html
We need to make it a Accessibility app by setting uiAcess=true in the
manifest, sign the executable, and placing it in a secure location
(e.g. Program Files) as described below:
Set uiAccess=true in the manifest Set this option in Visual Studio
by setting Linker | Manifest File | UAC Bypass UI Protection to Yes
Sign the executable See
https://learn.microsoft.com/en-us/previous-versions/bb756995(v=msdn.10)
Place it in a secure location See
https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-account-control-only-elevate-uiaccess-applications-that-are-installed-in-secure-locations
I placed the application in a secure location before I signed it and used the following commands to create the certificate and sign the application.
makecert /n "CN=Company, O=Company, C=SE" /r /pe /h 0 /eku "1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13" /e 01/01/2021 /sv Company.pvk Company.cer
Pvk2Pfx /pvk Company.pvk /pi "password" /spc Company.cer /pfx Company.pfx /po "password" /pi "password"
and finally
signtool sign /f "Company.pfx" /p "password" "application".exe
And that's it!
Related
I have a maui app with blazor and I'm creating a custom titlebar.
I about to close the maui app, using on blazor Application.Current.Quit();
Now how can i minimize and move maui app
My code blazor
private void MoveWindow()
{
}
private void MinimizeWindow()
{
}
private void CloseWindow() {
Application.Current.Quit();
}
Maui already has a function to minimize the application.
Use the following in your blazor:
private void MinimizeWindow()
{
#if WINDOWS
var Window = App.Current.Windows.First();
var nativeWindow = Window.Handler.PlatformView;
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
WindowId WindowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
AppWindow appWindow = AppWindow.GetFromWindowId(WindowId);
var p = appWindow.Presenter as OverlappedPresenter;
p.Minimize();
#endif
}
You need to call native code for that.
Add a reference to PInvoke.User32 package:
<PackageReference Include="PInvoke.User32" Version="0.7.104" Condition="$([MSBuild]::IsOSPlatform('windows'))"/>
Minimize
As an example upon a button click we minimize the window:
void MinimizeWindow(object sender, EventArgs e)
{
#if WINDOWS
var mauiWindow = App.Current.Windows.First();
var nativeWindow = mauiWindow.Handler.PlatformView;
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
PInvoke.User32.ShowWindow(windowHandle, PInvoke.User32.WindowShowStyle.SW_MINIMIZE);
#endif
}
Move
void MoveWindow(object sender, EventArgs e)
{
#if WINDOWS
var mauiWindow = App.Current.Windows.First();
var nativeWindow = mauiWindow.Handler.PlatformView;
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
PInvoke.RECT rect;
PInvoke.User32.GetWindowRect(windowHandle, out rect);
(var width, var height) = GetWinSize(rect);
PInvoke.User32.MoveWindow(windowHandle, 50, 0, width, height, true);
#endif
}
(int width, int height) GetWinSize(PInvoke.RECT rect) =>
(rect.right - rect.left, rect.bottom - rect.top);
It's possible to resize the window with MoveWindow(), but since the aim in the question is to move the window only, then we supply the values of the current window's width and height as parameters.
Ps: Unfortunately I didn't find a simpler solution to get the window dimensions.
EDIT
A better alternative is to use AppWindow.Move(PointInt32):
#if WINDOWS
WindowId WindowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
AppWindow appWindow = AppWindow.GetFromWindowId(WindowId);
appWindow.Move(new Windows.Graphics.PointInt32(x,y))
#endif
x and y are the coordinate for the desired new position. The origin (0,0) is the left upper corner of the screen.
For information about the screen dimensions:
Native api: Open app always in the center of the display - Windows 11 (WinUi 3)
Maui: var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
I have a button that opens a New Window.
I want to fit the New Window to the dynamic content inside.
Then position it Center, relative to Main Window. So it follows the Main Window's location.
Not WindowStartupLocation.CenterScreen.
This is what I'm using. It doesn't quite work right.
XAML
The New Window is originally set to 900x500, but is overridden by SizeToContent.
<Window x:Class="MyProgram.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="New Window"
Width="900"
Height="500">
C#
This Button is on Main Window.
I use SizeToContent.Width.
newWindow.Width does not use the SizeToContent's width, but instead detects the XAML width 900.
This causes the New Window to always be off center.
I tried setting the XAML width to Auto or 1, it still goes off center a different direction.
I tried using double width = Convert.ToInt32(SizeToContent.Width); but it says the width is 1.
I ran newWindow.UpdateLayout() but it didn't work. https://stackoverflow.com/a/2149676/6806643
public static NewWindow newWindow;
private Boolean IsNewWindowOpened = false;
private void btnNeWindow_Click(object sender, RoutedEventArgs e)
{
// Check if Window is already open
if (IsNewWindowOpened) return;
newWindow = new NewWindow(this);
// Only allow 1 Window instance
newWindow.ContentRendered += delegate { IsNewWindowOpened = true; };
newWindow.Closed += delegate { IsNewWindowOpened = false; };
// Keep Window on Top
newWindow.Owner = Window.GetWindow(this);
// Fit Window to Content
newWindow.SizeToContent = SizeToContent.Width;
// Update Layout
newWindow.UpdateLayout()
// Detect which screen we're on
var allScreens = System.Windows.Forms.Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
if (thisScreen == null) thisScreen = allScreens.First();
// Position Relative to MainWindow
newWindow.Left = Math.Max((this.Left + (this.Width - newWindow.Width) / 2), thisScreen.WorkingArea.Left);
newWindow.Top = Math.Max((this.Top + (this.Height - newWindow.Height) / 2), thisScreen.WorkingArea.Top);
// Open Window
newWindow.Show();
}
Examples
Off Center
Correctly Centered
Here is what I've done. Let me know if you have any improvements.
Main Window
Open New Window Button
public static NewWindow newWindow;
private Boolean IsNewWindowOpened = false;
private void btnNeWindow_Click(object sender, RoutedEventArgs e)
{
if (IsPreviewWindowOpened) return;
// Open Preview Window
previewWindow = new Preview(this);
// Only allow 1 Window instance
previewWindow.ContentRendered += delegate { IsPreviewWindowOpened = true; };
previewWindow.Closed += delegate { IsPreviewWindowOpened = false; };
// Keep Window on Top
previewWindow.Owner = Window.GetWindow(this);
// Size to Content
previewWindow.SizeToContent = SizeToContent.Width;
// Open Window
previewWindow.Show();
}
New Window
XAML
Window_Loaded
<Window x:Class="MyProgram.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="New Window"
Width="900"
Height="500"
Loaded="Window_Loaded">
C#
Size Window to fit dynamic content.
Position Center, relative to Main Window.
Also nudges window back on screen if going off.
private void Window_Loaded(object sender, EventArgs e)
{
// Detect which screen we're on
var allScreens = System.Windows.Forms.Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
if (thisScreen == null) thisScreen = allScreens.First();
// Position Relative to MainWindow
this.Left = Math.Max((mainwindow.Left + (mainwindow.Width - this.Width) / 2), thisScreen.WorkingArea.Left);
this.Top = Math.Max((mainwindow.Top + (mainwindow.Height - this.Height) / 2), thisScreen.WorkingArea.Top);
}
I'm trying to write a magnifier application using a winform or wpf windows. The idea is to drag the window over a spot on the screen and magnify it. I know it exists commercially but need to build a customized version. The challenge I'm having is to capture the screen image behind the active application.
I have found code to capture a screen image below. But it includes the active window
{
Rectangle bounds = new Rectangle(this.Location, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(this.Location, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
}
Adding statements to hide the active application correct the image capture, but introduce a screen flicker which I'd like to avoid. (modified code below)
{
this.StartPosition = FormStartPosition.Manual; // get current window location
Point cur = this.Location;
this.Location = new Point(-500, -500); // hide the active app off screen.
Rectangle bounds = new Rectangle(cur, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(cur, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
this.Location = cur; // restore application location
}
Can someone suggest an alternative to capture a screen region, behind an active windows?
Thx.
Wrapping the Magnification API is pretty useful. I created a Winforms control that does this. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto a form.
The TrackMouse property controls which part of the screen is shown magnified. Set to False, it magnifies the area covered by the control, making it act like a magnifying glass. Set to True, it will operate like Windows' Magnifier, following the mouse.
The Magnification property controls the amount of magnification. You can already adjust it by using the mouse wheel.
The form you drop it should have its TopMost property set to True. You might want to tinker with its Region property to make it resemble a spyglass.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class Magnifier : Control {
public Magnifier() {
if (!MagInitialize()) throw new NotSupportedException();
timer = new Timer { Interval = 45 };
timer.Tick += (o, ea) => { if (trackMouse) setSource(false); else Invalidate(); };
}
[DefaultValue(false)]
public bool TrackMouse {
get { return trackMouse; }
set { trackMouse = value; setSource(false); }
}
[DefaultValue(2.0f)]
public float Magnification {
get { return magnification; }
set { magnification = Math.Max(1, value); setSource(true); }
}
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
if (!this.DesignMode) {
cp.ClassName = "Magnifier";
//cp.Style |= MS_SHOWMAGNIFIEDCURSOR;
this.SetStyle(ControlStyles.UserPaint, true);
}
return cp;
}
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (!this.DesignMode) {
setSource(true);
this.FindForm().LocationChanged += ParentLocationChanged;
timer.Start();
}
}
protected override void Dispose(bool disposing) {
if (disposing) {
var frm = this.FindForm();
if (frm != null) frm.LocationChanged -= ParentLocationChanged;
timer.Dispose();
MagUninitialize();
}
base.Dispose(disposing);
}
private void ParentLocationChanged(object sender, EventArgs e) {
if (!trackMouse) setSource(false);
}
protected override void OnSizeChanged(EventArgs e) {
setSource(false);
base.OnSizeChanged(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
this.Magnification += e.Delta / 100f;
((HandledMouseEventArgs)e).Handled = true;
}
private void setSource(bool newmag) {
if (!this.IsHandleCreated || this.DesignMode) return;
if (newmag) {
var xform = new MAGTRANSFORM();
xform.v11 = xform.v22 = magnification;
xform.v33 = 1.0f;
MagSetWindowTransform(this.Handle, ref xform);
}
Point center;
if (trackMouse) center = Cursor.Position;
else {
var rc = this.RectangleToScreen(this.Bounds);
center = new Point(rc.Left + rc.Width / 2, rc.Top + rc.Height / 2);
}
var scr = Screen.FromPoint(center);
var rect = new RECT();
rect.left = Math.Max(scr.Bounds.Left, center.X - (int)(this.Width / magnification / 2));
rect.top = Math.Max(scr.Bounds.Top, center.Y - (int)(this.Height / magnification / 2));
rect.right = rect.left + (int)(this.Width / magnification);
if (rect.right > scr.Bounds.Right) {
rect.right = scr.Bounds.Right;
rect.left = rect.right - (int)(this.Width / magnification);
}
rect.bottom = center.Y + (int)(this.Height / magnification);
if (rect.bottom > scr.Bounds.Bottom) {
rect.bottom = scr.Bounds.Bottom;
rect.top = rect.bottom - (int)(this.Height / magnification);
}
MagSetWindowSource(this.Handle, ref rect);
this.Invalidate();
}
private Timer timer;
private bool trackMouse;
private float magnification = 2.0f;
private struct RECT {
public int left, top, right, bottom;
}
private struct MAGTRANSFORM {
public float v11, v12, v13;
public float v21, v22, v23;
public float v31, v32, v33;
}
[DllImport("magnification.dll")]
private static extern bool MagInitialize();
[DllImport("magnification.dll")]
private static extern bool MagUninitialize();
[DllImport("magnification.dll")]
private static extern bool MagSetWindowSource(IntPtr hWnd, ref RECT rc);
[DllImport("magnification.dll")]
private static extern bool MagSetWindowTransform(IntPtr hWnd, ref MAGTRANSFORM xform);
private const int MS_SHOWMAGNIFIEDCURSOR = 1;
private const int MS_CLIPAROUNDCURSOR = 2;
private const int MS_INVERTCOLORS = 4;
}
I'm trying to place an object on a random location depending on the window size.
LayoutRoot is the name of the grid it's placed in.
//Give Dot a random position
int left = random.Next(LayoutRoot.MinWidth, LayoutRoot.MaxWidth);
int top = random.Next(-900, 900);
Dot.Margin = new Thickness(left, top, 0, 0);
Error on LayoutRoot.MinWidth & MaxWidth: Cannot convert double to int
tried
//Give Dot a random position
double left = random.NextDouble(LayoutRoot.MinWidth, LayoutRoot.MaxWidth);
double top = random.Next(-900, 900);
Dot.Margin = new Thickness(left, top, 0, 0);
Error on NextDouble: Method NextDouble takes 2 arguments
Ok going to up this one up a bit. Modified your code. This code doesn't assume any predefined height or width, it gets that based on the current size of the LayoutRoot. It also offsets the Dot size so it doesn't fall off the screen.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="LayoutRoot">
<Ellipse Width="2" Height="2" Fill="Black" x:Name="Dot"></Ellipse>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
private static Random random = new Random();
public MainWindow()
{
InitializeComponent();
// Don't move dot until the window is loaded, not necessary but generally you don't want to hold up the window from displaying.
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
// The farthest left the dot can be
double minLeft = 0;
// The farthest right the dot can be without it going off the screen
double maxLeft = LayoutRoot.ActualWidth - Dot.Width;
// The farthest up the dot can be
double minTop = 0;
// The farthest down the dot can be without it going off the screen
double maxTop = LayoutRoot.ActualHeight - Dot.Height;
double left = RandomBetween(minLeft, maxLeft);
double top = RandomBetween(minTop, maxTop);
Dot.Margin = new Thickness(left, top, 0, 0);
}
private double RandomBetween(double min, double max)
{
return random.NextDouble() * (max - min) + min;
}
}
}
this might work.
double mWidth = LayoutRoot.MinWidth;
double mxWidth = LayoutRoot.MaxWidth;
double left = random.NextDouble(mWidth, mxWidth);
double top = random.Next(-900, 900);
Dot.Margin = new Thickness(left, top, 0, 0);
}
So after looking at the answer given by Kelly I had a similar issue where the object would not display after loading. My goal was to have the welcome screen of an application randomly move every 10 seconds. So following his design there was an issue where because the grid did not have an actual length due to the lack of column/row definitions we have to force the app to recalculate size at every attempt to randomize.
Here is the XAML I used:
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="WelcomeTextBlock"
Height="200"
Style="{StaticResource WelcomeTextBlock}">
Welcome!
</TextBlock>
<Ellipse Width="2" Height="2" x:Name="Dot"/>
</Grid>
And more importantly the actual code-behind:
public partial class WelcomePage : Page
{
private static Random Rnd = new Random();
private static DispatcherTimer _timer;
public WelcomePage()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnClosing;
}
private void OnLoaded(object sender, RoutedEventArgs args)
{
this.LayoutRoot.Margin = GetNewMargin();
_timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(5)};
_timer.Tick += MoveWelcome;
_timer.Start();
}
private void MoveWelcome(object sender, EventArgs e)
{
this.LayoutRoot.Margin = GetNewMargin();
}
private Thickness GetNewMargin()
{
this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.Arrange(new Rect(0, 0, this.DesiredSize.Width, this.DesiredSize.Height));
var maxLeft = this.LayoutRoot.ActualWidth - Dot.Width;
var maxTop = LayoutRoot.ActualHeight - Dot.Height;
var left = RandomBetween(0, maxLeft);
var top = RandomBetween(0, maxTop);
return new Thickness(left, top, 0 ,0);
}
private static void OnClosing(object sender, RoutedEventArgs args)
{
_timer.Stop();
}
private static double RandomBetween(double min, double max) => Rnd.NextDouble() * (max - min) + max;
Besides the timer functionality the major change here is that when the margin thickness is being calculated we force the window, or in my case page to update the measurements of the screen and arrange the items correctly.
I'm trying to write a magnifier application using a winform or wpf windows. The idea is to drag the window over a spot on the screen and magnify it. I know it exists commercially but need to build a customized version. The challenge I'm having is to capture the screen image behind the active application.
I have found code to capture a screen image below. But it includes the active window
{
Rectangle bounds = new Rectangle(this.Location, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(this.Location, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
}
Adding statements to hide the active application correct the image capture, but introduce a screen flicker which I'd like to avoid. (modified code below)
{
this.StartPosition = FormStartPosition.Manual; // get current window location
Point cur = this.Location;
this.Location = new Point(-500, -500); // hide the active app off screen.
Rectangle bounds = new Rectangle(cur, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(cur, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
this.Location = cur; // restore application location
}
Can someone suggest an alternative to capture a screen region, behind an active windows?
Thx.
Wrapping the Magnification API is pretty useful. I created a Winforms control that does this. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto a form.
The TrackMouse property controls which part of the screen is shown magnified. Set to False, it magnifies the area covered by the control, making it act like a magnifying glass. Set to True, it will operate like Windows' Magnifier, following the mouse.
The Magnification property controls the amount of magnification. You can already adjust it by using the mouse wheel.
The form you drop it should have its TopMost property set to True. You might want to tinker with its Region property to make it resemble a spyglass.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class Magnifier : Control {
public Magnifier() {
if (!MagInitialize()) throw new NotSupportedException();
timer = new Timer { Interval = 45 };
timer.Tick += (o, ea) => { if (trackMouse) setSource(false); else Invalidate(); };
}
[DefaultValue(false)]
public bool TrackMouse {
get { return trackMouse; }
set { trackMouse = value; setSource(false); }
}
[DefaultValue(2.0f)]
public float Magnification {
get { return magnification; }
set { magnification = Math.Max(1, value); setSource(true); }
}
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
if (!this.DesignMode) {
cp.ClassName = "Magnifier";
//cp.Style |= MS_SHOWMAGNIFIEDCURSOR;
this.SetStyle(ControlStyles.UserPaint, true);
}
return cp;
}
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (!this.DesignMode) {
setSource(true);
this.FindForm().LocationChanged += ParentLocationChanged;
timer.Start();
}
}
protected override void Dispose(bool disposing) {
if (disposing) {
var frm = this.FindForm();
if (frm != null) frm.LocationChanged -= ParentLocationChanged;
timer.Dispose();
MagUninitialize();
}
base.Dispose(disposing);
}
private void ParentLocationChanged(object sender, EventArgs e) {
if (!trackMouse) setSource(false);
}
protected override void OnSizeChanged(EventArgs e) {
setSource(false);
base.OnSizeChanged(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
this.Magnification += e.Delta / 100f;
((HandledMouseEventArgs)e).Handled = true;
}
private void setSource(bool newmag) {
if (!this.IsHandleCreated || this.DesignMode) return;
if (newmag) {
var xform = new MAGTRANSFORM();
xform.v11 = xform.v22 = magnification;
xform.v33 = 1.0f;
MagSetWindowTransform(this.Handle, ref xform);
}
Point center;
if (trackMouse) center = Cursor.Position;
else {
var rc = this.RectangleToScreen(this.Bounds);
center = new Point(rc.Left + rc.Width / 2, rc.Top + rc.Height / 2);
}
var scr = Screen.FromPoint(center);
var rect = new RECT();
rect.left = Math.Max(scr.Bounds.Left, center.X - (int)(this.Width / magnification / 2));
rect.top = Math.Max(scr.Bounds.Top, center.Y - (int)(this.Height / magnification / 2));
rect.right = rect.left + (int)(this.Width / magnification);
if (rect.right > scr.Bounds.Right) {
rect.right = scr.Bounds.Right;
rect.left = rect.right - (int)(this.Width / magnification);
}
rect.bottom = center.Y + (int)(this.Height / magnification);
if (rect.bottom > scr.Bounds.Bottom) {
rect.bottom = scr.Bounds.Bottom;
rect.top = rect.bottom - (int)(this.Height / magnification);
}
MagSetWindowSource(this.Handle, ref rect);
this.Invalidate();
}
private Timer timer;
private bool trackMouse;
private float magnification = 2.0f;
private struct RECT {
public int left, top, right, bottom;
}
private struct MAGTRANSFORM {
public float v11, v12, v13;
public float v21, v22, v23;
public float v31, v32, v33;
}
[DllImport("magnification.dll")]
private static extern bool MagInitialize();
[DllImport("magnification.dll")]
private static extern bool MagUninitialize();
[DllImport("magnification.dll")]
private static extern bool MagSetWindowSource(IntPtr hWnd, ref RECT rc);
[DllImport("magnification.dll")]
private static extern bool MagSetWindowTransform(IntPtr hWnd, ref MAGTRANSFORM xform);
private const int MS_SHOWMAGNIFIEDCURSOR = 1;
private const int MS_CLIPAROUNDCURSOR = 2;
private const int MS_INVERTCOLORS = 4;
}