.
GDI+ FAQ
Skip Navigation LinksWelcome : Windows Forms Tips and Tricks : Creating a Snap-to-grid effect

Snap to grid effects

 

Isn't it annoying when the mouse is too sensitive for placing objects on screen in nice neat rows? The snap-to-grid feature is much requested and fairly simple to accomplish.

The trick in this feature is to use some simple math trickery to get the mouse position to go to the nearest neighboring point in the grid. There is, as usual, more than one way to do this but my favourite is to override the mouse handling methods, condition the mouse position and then allow the control events to broadcast the modified mouse positions to code which is wired to them.

Before looking at the code proper just examine in detail the method that's used to fool the mouse events into broadcasting what we want it to. Basically, every mouse event has a MouseEventArgs object which reports the position of the mouse. By creating new mouse event arguments containing massaged data we can fool anything attached to the mouse events that the mouse is somewhere else. The following routine takes a mouse position, modifies it according to the grid-snap criteria and sends back a modified MouseEventArgs object for use elsewhere.

    protected MouseEventArgs MouseSnap(MouseEventArgs e)

    {

      int px, py;

 

      if(_snap)

      {

        px=(int)(((float)e.X/_snapX)+0.5f)*_snapX;

        py=(int)(((float)e.Y/_snapY)+0.5f)*_snapY;

      }

      else

      {

        px=e.X;

        py=e.Y;

      }

 

      MouseEventArgs t=new MouseEventArgs(e.Button,e.Clicks,px,py,e.Delta);

 

      return t;

    }

 

As you can see, the snap distance and indeed, whether snap is applied at-all, is controlled by properties in a control. The following listing shows that control in it's entirety. You can control the X and Y snap settings, using SnapX and SnapY and turn the effect on or off using the Snap property.

 

  public class GridSnap : ScrollableControl

  {

    int _snapX=8;

    public int SnapX

    {

      get{return _snapX;}

      set{_snapX=value;}

    }

 

    int _snapY=8;

    public int SnapY

    {

      get{return _snapY;}

      set{_snapY=value;}

    }

 

    bool _snap;

    public bool Snap

    {

      get{return _snap;}

      set{_snap=value;}

    }

 

 

 

    public GridSnap()

    {

    }

 

    protected MouseEventArgs MouseSnap(MouseEventArgs e)

    {

      int px, py;

 

      if(_snap)

      {

        px=(int)(((float)e.X/_snapX)+0.5f)*_snapX;

        py=(int)(((float)e.Y/_snapY)+0.5f)*_snapY;

      }

      else

      {

        px=e.X;

        py=e.Y;

      }

 

      MouseEventArgs t=new MouseEventArgs(e.Button,e.Clicks,px,py,e.Delta);

 

      return t;

    }

 

    protected override void OnMouseMove(MouseEventArgs e)

    {

      base.OnMouseMove(this.MouseSnap(e));

    }

 

    protected override void OnMouseDown(MouseEventArgs e)

    {

      base.OnMouseDown(this.MouseSnap(e));

    }

 

    protected override void OnMouseUp(MouseEventArgs e)

    {

      base.OnMouseUp(this.MouseSnap(e));

    }

 

  }

 

Using this control is demonstrated in the following application. This lets you draw blobs on a form with the grid-snap set.

  /// <summary>

  /// Summary description for Form1.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

    private WellFormed.GridSnap gridSnap1;

    /// <summary>

    /// Required designer variable.

    /// </summary>

    private System.ComponentModel.Container components = null;

 

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

    }

 

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    #region Windows Form Designer generated code

    /// <summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

      this.gridSnap1 = new WellFormed.GridSnap();

      this.SuspendLayout();

      //

      // gridSnap1

      //

      this.gridSnap1.Dock = System.Windows.Forms.DockStyle.Fill;

      this.gridSnap1.Location = new System.Drawing.Point(0, 0);

      this.gridSnap1.Name = "gridSnap1";

      this.gridSnap1.Size = new System.Drawing.Size(432, 318);

      this.gridSnap1.Snap = true;

      this.gridSnap1.SnapX = 32;

      this.gridSnap1.SnapY = 32;

      this.gridSnap1.TabIndex = 0;

      this.gridSnap1.Text = "gridSnap1";

      this.gridSnap1.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.gridSnap1_KeyPress);

      this.gridSnap1.Click += new System.EventHandler(this.gridSnap1_Click);

      this.gridSnap1.Paint += new System.Windows.Forms.PaintEventHandler(this.gridSnap1_Paint);

      this.gridSnap1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.gridSnap1_KeyDown);

      this.gridSnap1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gridSnap1_MouseMove);

      //

      // Form1

      //

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

      this.ClientSize = new System.Drawing.Size(432, 318);

      this.Controls.Add(this.gridSnap1);

      this.Name = "Form1";

      this.Text = "Form1";

      this.ResumeLayout(false);

 

    }

    #endregion

 

    /// <summary>

    /// The main entry point for the application.

    /// </summary>

    [STAThread]

    static void Main()

    {

      Application.Run(new Form1());

    }

 

    ArrayList points=new ArrayList();

 

    private void gridSnap1_Click(object sender, System.EventArgs e)

    {

      points.Add(pos);

      this.gridSnap1.Invalidate();

    }

 

    private void gridSnap1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      foreach(Point p in points)

      {

        e.Graphics.FillEllipse(Brushes.Red, p.X-2,p.Y-2,4,4);

      }

 

      e.Graphics.DrawEllipse(Pens.Teal,pos.X-5,pos.Y-5,10,10);

    }

 

    Point pos;

 

    private void gridSnap1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)

    {

      pos=new Point(e.X,e.Y);

      this.gridSnap1.Invalidate();

    }

 

    private void gridSnap1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)

    {

      MessageBox.Show(e.KeyChar.ToString());

    }

 

    private void gridSnap1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)

    {

      MessageBox.Show(e.KeyData.ToString());

    

    }

  }

 

Return to Windows Forms Tips and Tricks

Visit the GDI+ FAQ

Copyright Bob Powell 2004. All rights reserved.

 

Sponsored By
DaraizeTechnologies.com

&
Proteus Groupe

Bob Powell

Create your badge

Copyright © Bob Powell 2000-2012.  All rights reserved.