Introduction
In the previous installment, “Creating a Magnifier in .NET, Part 1: Structure,” we created the structure for this project. We set up the Methods and Properties needed. If you have not read Part 1 yet, please do so before continuing.
All we need to do now is to make this work with a Form, so let’s not waste any more time and get started.
Practical
Open the code window for your Form, and add the next few fields to it.
private Form frmMag; private Thread tBackground; private System.Windows.Forms.Timer tmrMag; private IntPtr iptrMag; private float fMagVal; private RECT rectMagWin = new RECT(); private bool blnInit; private bool blnNormal = true; private bool blnHidden = true;
Add the next few Enumerations:
private enum WStyle { ExStyle = -20 } private enum WType { Transparent = 0x20, Layered = 0x80000 } private enum WCol { ColorKey = 0x1, Alpha = 0x1 }
The Enumerations contain the settings for the Window style, its appearance, and its main color. Now, let’s add some more APIs:
[DllImport("user32.dll", EntryPoint = "GetWindowLong")] private static extern int GetWindowLong(IntPtr hWnd, WStyle nIndex); [DllImport("user32.dll", EntryPoint = "SetWindowLong")] private static extern int SetWindowLong(IntPtr hWnd, WStyle nIndex, int dwNewLong); [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")] private static extern bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte alpha, WCol dwFlags); [DllImport("user32.dll")] private static extern short GetAsyncKeyState(Keys vKey);
Add the Constructor to initialize the variables to default values:
public Form1() { InitializeComponent(); frmMag = this; fMagVal = 2.0f; CreateWindow(); frmMag.Resize += new EventHandler(Form_Resize); frmMag.FormClosing += new FormClosingEventHandler(Form_FormClosing); tmrMag = new System.Windows.Forms.Timer(); tmrMag.Tick += new EventHandler(Timer_Tick); blnInit = APIMethods.MagInitialize(); if (blnInit) { Setup(); tmrMag.Interval = APIMethods.USER_TIMER_MINIMUM; tmrMag.Enabled = true; } DetermineKeysThread(); } private void CreateWindow() { DoubleBuffered = true; WindowState = FormWindowState.Normal; StartPosition = FormStartPosition.CenterScreen; ShowInTaskbar = false; }
Determine which key is pressed with the next methods:
private void DetermineKeysThread() { tBackground = new Thread(() => DetermineKeys()) { IsBackground = true, Priority = ThreadPriority.Normal }; tBackground.Start(); } private void DetermineKeys() { BeginInvoke(new MethodInvoker(delegate { Hide(); })); while (true) { Thread.Sleep(10); foreach (Keys k in Enum.GetValues(typeof(Keys))) { if (((GetAsyncKeyState(k) & (1 << 15)) != 0)) { if (k == Keys.MButton) { if (blnHidden) { BeginInvoke(new MethodInvoker(delegate { Show(); })); Thread.Sleep(500); blnHidden = false; } else { BeginInvoke(new MethodInvoker(delegate { Hide(); })); Thread.Sleep(500); blnHidden = true; } } if (k == Keys.Add) { BeginInvoke(new MethodInvoker(delegate { Magnification++; })); Thread.Sleep(500); } if (k == Keys.Subtract) { BeginInvoke(new MethodInvoker(delegate { if (Magnification > 1f) { Magnification--; } })); Thread.Sleep(500); } if (k == Keys.Multiply) { if (blnNormal) { blnNormal = false; BeginInvoke(new MethodInvoker(delegate { WindowState = FormWindowState.Maximized; })); Thread.Sleep(500); } else { blnNormal = true; BeginInvoke(new MethodInvoker(delegate { WindowState = FormWindowState.Normal; })); Thread.Sleep(500); } } if (k == Keys.F10) { Application.Exit(); } } } } }
Add the next methods to override the Form’s Shown method, and the method to resize the form:
protected override void OnShown(EventArgs e) { base.OnShown(e); int wl = GetWindowLong(Handle, WStyle.ExStyle); wl = wl | 0x80000 | 0x20; SetWindowLong(Handle, WStyle.ExStyle, wl); SetLayeredWindowAttributes(Handle, 0, 128, WCol.Alpha); } protected virtual void ResizeMag() { if (blnInit && (iptrMag != IntPtr.Zero)) { APIMethods.GetClientRect(frmMag.Handle, ref rectMagWin); APIMethods.SetWindowPos(iptrMag, IntPtr.Zero, rectMagWin.left, rectMagWin.top, rectMagWin.right, rectMagWin.bottom, 0); } } public virtual void UpdateMag() { if ((!blnInit) || (iptrMag == IntPtr.Zero)) { return; } RECT rctSource = new RECT(); Point pPos = new Point(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2); int iWidth = (int)((rectMagWin.right - rectMagWin.left) / fMagVal); int iHeight = (int)((rectMagWin.bottom - rectMagWin.top) / fMagVal); rctSource.left = pPos.X - iWidth / 2; rctSource.top = pPos.Y - iHeight / 2; if (rctSource.left < 0) { rctSource.left = 0; } if (rctSource.left > APIMethods.GetSystemMetrics (APIMethods.SM_CXSCREEN) - iWidth) { rctSource.left = APIMethods.GetSystemMetrics (APIMethods.SM_CXSCREEN) - iWidth; } rctSource.right = rctSource.left + iWidth; if (rctSource.top < 0) { rctSource.top = 0; } if (rctSource.top > APIMethods.GetSystemMetrics (APIMethods.SM_CYSCREEN) - iHeight) { rctSource.top = APIMethods.GetSystemMetrics (APIMethods.SM_CYSCREEN) - iHeight; } rctSource.bottom = rctSource.top + iHeight; if (frmMag == null) { tmrMag.Enabled = false; return; } if (frmMag.IsDisposed) { tmrMag.Enabled = false; return; } APIMethods.MagSetWindowSource(iptrMag, rctSource); APIMethods.SetWindowPos(frmMag.Handle, APIMethods.HWND_TOPMOST, 0, 0, 0, 0, (int)SetWindowPosFlags.SWP_NOACTIVATE | (int)SetWindowPosFlags.SWP_NOMOVE | (int)SetWindowPosFlags.SWP_NOSIZE); APIMethods.InvalidateRect(iptrMag, IntPtr.Zero, true); } private float Magnification { get { return fMagVal; } set { if (fMagVal != value) { fMagVal = value; Transform tMatrix = new Transform(fMagVal); APIMethods.MagSetWindowTransform(iptrMag, ref tMatrix); } } } protected void Setup() { if (!blnInit) { return; } IntPtr hInst; hInst = APIMethods.GetModuleHandle(null); frmMag.AllowTransparency = true; frmMag.TransparencyKey = System.Drawing.Color.Empty; frmMag.Opacity = 255; APIMethods.GetClientRect(frmMag.Handle, ref rectMagWin); iptrMag = APIMethods.CreateWindow((int)ExtendedWindowStyles .WS_EX_TRANSPARENT, APIMethods.MAGNIFIER, "Form1", (int)WindowStyles.WS_CHILD | (int)WindowStyles.WS_VISIBLE, rectMagWin.left, rectMagWin.top, rectMagWin.right, rectMagWin.bottom, frmMag.Handle, IntPtr.Zero, hInst, IntPtr.Zero); if (iptrMag == IntPtr.Zero) { return; } Transform tMatrix = new Transform(fMagVal); APIMethods.MagSetWindowTransform(iptrMag, ref tMatrix); } protected void RemoveMag() { if (blnInit) { APIMethods.MagUninitialize(); } }
Finally, add the last few methods:
private void Form_FormClosing(object sender, FormClosingEventArgs e) { tmrMag.Enabled = false; } private void Form_Resize(object sender, EventArgs e) { ResizeMag(); } private void Timer_Tick(object sender, EventArgs e) { UpdateMag(); }
Conclusion
As developers, we are so fortunate to have Windows on our side, because making use of its built-in APIs can give us more power. I hope you have enjoyed this little series. Until next time, happy coding!