vbAccelerator - Contents of code file: SizingMovingSubclass.cs

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace vbAccelerator.Components.SubClassExamples
{

   // note that in this example I'm using Interfaces
   // to notify the caller of a change.  However, you
   // could equally use events for the notifications,
   // there is just a little more overhead that way.

   #region Sizing/Moving Subclass
   /// <summary>
   /// Enumeration of reason for WM_SIZE message
   /// </summary>
   public enum SizedReasonTypes
   {
      /// <summary>
      /// Message is sent to all pop-up windows when some 
      /// other window is maximized. 
      /// </summary>
      SIZE_MAXHIDE = 4,
      /// <summary>
      /// The window has been maximized. 
      /// </summary>
      SIZE_MAXIMIZED = 2,
      /// <summary>
      /// Message is sent to all pop-up windows when some other window 
      /// has been restored to its former size. 
      /// </summary>
      SIZE_MAXSHOW  = 3,
      /// <summary>
      /// The window has been minimized. 
      /// </summary>
      SIZE_MINIMIZED = 1,
      /// <summary>
      /// The window has been resized, but neither the SIZE_MINIMIZED 
      /// nor SIZE_MAXIMIZED value applies. 
      /// </summary>
      SIZE_RESTORED = 0
   }

   /// <summary>
   /// Enumeration of location from which sizing is taking place
   /// </summary>
   public enum SizingLocationTypes
   {
      /// <summary>
      /// Bottom edge 
      /// </summary>
      WMSZ_BOTTOM = 6,
      /// <summary>
      /// Bottom-left corner 
      /// </summary>
      WMSZ_BOTTOMLEFT = 7,
      /// <summary>
      /// Bottom-right corner 
      /// </summary>
      WMSZ_BOTTOMRIGHT = 8,
      /// <summary>
      /// Left edge 
      /// </summary>
      WMSZ_LEFT = 1,
      /// <summary>
      /// Right edge 
      /// </summary>
      WMSZ_RIGHT = 2,
      /// <summary>
      /// Top edge
      /// </summary>
      WMSZ_TOP = 3,
      /// <summary>
      /// Top-left corner 
      /// </summary>
      WMSZ_TOPLEFT = 4,
      /// <summary>
      /// Top-right corner 
      /// </summary>
      WMSZ_TOPRIGHT = 5
   }

   /// <summary>
   /// Interface for receiving Sizing and Moving notifications
   /// </summary>
   public interface ISizingMovingNotify
   {
      /// <summary>
      /// Notifies of a window sizing attempt.
      /// </summary>
      /// <param name="rectangle">Proposed new window rectangle; can be
       modified</param>
      /// <param name="sizingLocation">Location from which sizing is taking
       place</param>
      void Sizing(ref System.Drawing.Rectangle rectangle, 
         SizingLocationTypes sizingLocation);
      /// <summary>
      /// Notifies of a window moving attempt.
      /// </summary>
      /// <param name="rectangle">Proposed new window rectangle; can be
       modified</param>
      void Moving(ref System.Drawing.Rectangle rectangle);
      /// <summary>
      /// Notifies of a window move.
      /// </summary>
      /// <param name="rectangle">New window rectangle.</param>
      void Moved(System.Drawing.Rectangle rectangle);
      /// <summary>
      /// Notifies of a windows change in size.
      /// </summary>
      /// <param name="rectangle">New window rectangle.</param>
      /// <param name="reason">Reason for size change</param>
      void Sized(System.Drawing.Rectangle rectangle, 
         SizedReasonTypes reason);
   }

   /// <summary>
   /// Class for intercepting sizing and moving events for
   /// a window.
   /// </summary>
   public class SizingMovingSubclass : 
      System.Windows.Forms.NativeWindow, 
      IDisposable
   {
      /// <summary>
      /// User defined data
      /// </summary>
      private object tag = null;
      /// <summary>
      /// Object to notify when window moves or sizes
      /// </summary>
      private ISizingMovingNotify notify = null;

      /// <summary>
      /// Message send during sizing operations
      /// </summary>
      private const int WM_SIZING  = 0x0214;
      /// <summary>
      /// Message send during moving operations
      /// </summary>
      private const int WM_MOVING  = 0x0216;
      /// <summary>
      /// Message sent after window has been moved.
      /// </summary>
      private const int WM_MOVE = 0x3;
      /// <summary>
      /// Message sent after window has been sized.
      /// </summary>
      private const int WM_SIZE = 0x5;

      /// <summary>
      /// Private structure for interoperability
      /// </summary>   
      private struct RECT
      {
         public int left;
         public int top;
         public int right;
         public int bottom;

         public override string ToString()
         {
            return string.Format("RECT: ({0},{1})-({2},{3})",
               this.left, this.top, this.right, this.bottom);
         }
      }

      [DllImport("user32")]
      private static extern int GetWindowRect(
         IntPtr handle,
         ref RECT rc);

      /// <summary>
      /// Gets/sets a user-defined object associated with the class
      /// </summary>
      public object Tag
      {
         get
         {
            return this.tag;
         }
         set
         {
            this.tag = value;
         }
      }

      /// <summary>
      /// Gets/sets the interface to notify of moving or sizing
      /// </summary>
      public ISizingMovingNotify Notify
      {
         get
         {
            return this.notify;
         }
         set
         {
            this.notify = value;
         }
      }

      /// <summary>
      /// Processes window messages and notifies of any sizing
      /// moving events.
      /// </summary>
      /// <param name="m"></param>
      protected override void WndProc(ref System.Windows.Forms.Message m)
      {
         RECT rc = new RECT();
         GetWindowRect(m.HWnd, ref rc);
         System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(
            rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);

         // If message is a size/move message and a notifier
         // is in place then notify it:
         switch (m.Msg)
         {
            case WM_SIZING:
               rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
               if (notify != null)
               {
                  rectangle = new System.Drawing.Rectangle(
                     rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
                  notify.Sizing(ref rectangle, 
                     (SizingLocationTypes)((int)m.WParam));   
                  rc.left = rectangle.Left;
                  rc.top = rectangle.Top;
                  rc.right = rectangle.Right;
                  rc.bottom = rectangle.Bottom;
               }
               Marshal.StructureToPtr(rc, m.LParam, true);
               m.Result = (IntPtr)1; // TRUE
               break;

            case WM_MOVING:
               rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
               if (notify != null)
               {
                  rectangle = new System.Drawing.Rectangle(
                     rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
                  notify.Moving(ref rectangle);   
                  rc.left = rectangle.Left;
                  rc.top = rectangle.Top;
                  rc.right = rectangle.Right;
                  rc.bottom = rectangle.Bottom;
               }
               Marshal.StructureToPtr(rc, m.LParam, true);
               m.Result = (IntPtr)1; // TRUE
               break;

            case WM_SIZE:
               base.WndProc(ref m);
               if (notify != null)
               {
                  // get width/height from lParam:                  
                  rectangle.Width = (int)((uint)m.LParam & 0xFFFF);
                  rectangle.Height = (int)(((uint)m.LParam & 0xFFFF0000) << 16);
                  notify.Sized(rectangle, 
                     (SizedReasonTypes)((int)m.WParam));
               }
               break;

            case WM_MOVE:
               base.WndProc(ref m);
               if (notify != null)
               {
                  rectangle.X = (int)((uint)m.LParam & 0xFFFF);
                  rectangle.Y = (int)(((uint)m.LParam & 0xFFFF0000) << 16);
                  notify.Moved(rectangle);
               }
               break;
            
            default:
               base.WndProc(ref m);
               break;
         }
      }

      /// <summary>
      /// Clears up resources associated with this object
      /// </summary>
      public void Dispose()
      {
         if (this.Handle != IntPtr.Zero)
         {
            this.ReleaseHandle();
         }
      }

      /// <summary>
      /// Creates a new instance of the SizingMovingSubclass
      /// object and starts checking for sizing and moving
      /// </summary>
      /// <param name="handle">Handle of window to check for
      /// sizing/moving</param>
      /// <param name="tag">User defined data</param>
      /// <param name="notify">Object to receive sizing/moving
      /// notifications</param>
      public SizingMovingSubclass(
         IntPtr handle, 
         object tag,
         ISizingMovingNotify notify)
      {
         this.AssignHandle(handle);
         this.tag = tag;
         this.notify = notify;
      }
   }
   #endregion

}