dcsimg

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

WEBINAR:
On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame


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!



About the Author

Hannes DuPreez

Hannes du Preez is an ex MVP for Visual Basic from 2008 to 2017. He loves technology and loves Visual Basic and C#. He loves writing articles and proving that Visual Basic is more powerful than what most believe. You are most welcome to reach him at: ojdupreez1978[at]gmail[dot]com

Related Articles

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date