Creating a Magnifier in .NET, Part 2: Making It Work | CodeGuru

Creating a Magnifier in .NET, Part 2: Making It Work

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, […]

Written By
Hannes DuPreez
Hannes DuPreez
Aug 20, 2019
1 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

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();
   }
Advertisement

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!

Hannes DuPreez

Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.