Click here to Skip to main content
Click here to Skip to main content
Go to top

MDISnap

, 29 Nov 2004
Rate this:
Please Sign up or sign in to vote.
Do your users a favor - add snapping edges to MDI child windows (or anywhere else).

Introduction

Although Multiple Document Interface (MDI) is less common these days, it's still a powerful choice for advanced applications. To make arranging the MDI child windows easier, MDISnap adds "magnetic edges"  to the MDI client area borders and other child windows, so arranging windows gets easier.

The best idea is to play with the sample application. Open some windows and tile them ([H] for horizontal, and [V] for vertical tile), then play around!

Update: I've added the following changes:

  • disable magnetic edges when using shift
  • magnetic edges disabled when move/size is initiated through the system menu (that's much better than the "jumping windows" I had before)
  • Holding down Ctrl, you can swap two windows
  • Brushed up the sample application a bit

The first two changes were submitted by a_zur. Thank you!

Using it

To use MDI Snapper, include this in your project:

  1. Declare a variable CMDISnapper m_mdiSnap accessible from all (e.g., in CFooApp or CMainFrame object)
  2. Adjust the snap wdith by calling m_mdiSnap.SetSnapWidth(int).
  3. For CChildFrame (and other CMDIChildWnd-derived classes you use), override WindowProc, and call m_mdiSnap.OnMessage before passing the message to the base class implementation.

Implementation Overview

struct SNAPINFO implements the logic for a single window, and holds required state variables. A typical call (e.g., in WM_SIZING / WM_MOVING) looks like this:

// ----- 1. Init -----
SNAPINFO sninf;
// old, new  position, size of catch area
sninf.Init(rectOld, rectNew, snapWidth);  

// ----- 2. provide horizontal/vertical "snap" lines -----
sninf.SnapVLine(0);                // snap to left border
sninf.SnapHLine(0);                // snap to top border
sninf.SnapVLine(clientArea.right); // snap to right border
sninf.SnapHLine(clientArea.bottom);// snap to bottom border 

// ... additional snap lines
// ... e.g. the borders of other MDI child windows

// ----- 3. finish  -----
sninf.EndSnap();
rectNew = sninf.rout; 

rectNew could then be used as corrected position of the window.

CMDISnapper implements exactly this for MDI child windows. It assumes and uses MDI client area coordinates in all places and does the appropriate conversions.

Integration in other projects / environments

SNAPINFO can be used in any Windows project and does the basic calculations. CMDISnapper uses MFC classes, but can be easily ported to e.g., ATL or Win32 API, as it does not rely on MFC specifics.

Documentation

struct SNAPINFO

  • Init(RECT const & oldRect, RECT const & newRect, DWORD snapWidth)

    Prepare calculation for adjusting a window while it is resized or moved.

  • Init(RECT const & r, DWORD snapWidth, bool moveOnly)

    Prepare calculation  for adjusting a single window, independent of changes.

  • void SnapHLine(long y)

    Specify the y coordinate of a horizontal line where one of the window edges could snap to. The line will be taken into consideration only if it's close enough to the top or bottom edge of the window.

  • void SnapVLine(long y)

    Guess what.

  • RECT & EndSnap()

    returns a reference to rect which contains the final coordinates after EndSnap was called.

class CMDISnapper

  • CMDISnapper(DWORD snapWidth = 8)

    MDISnapper constructor, at your service. If you already know what you want, you can specify a snap width.

  • void SetSnapWidth(DWORD snapWidth)
  • DWORD GetSnapWidth() const

    Set / retrieve snap width. It will be used with the next relevant message (currently, WM_SIZING and WM_MOVING) processed.

  • LRESULT OnMessage(CWnd * wnd, UINT msg, WPARAM wp, LPARAM lp)

    Call this function in the window proc of the window(s) that should snap. Pass the window, the message sent, and its parameters. Return value can be ignored. Forwards the following messages to specific handlers: WM_SIZING, WM_MOVING, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE.

  • void Sizing(CWnd * wnd, RECT & rnew)

    Called from WM_SIZING and WM_MOVING handler; retrieves the current rect, iterates through all visible sibling windows, and adjusts the new size/position rectangle specified in rnew.

Bugs, Limitations, Notes

  • Move via keyboard does not use the "magnetic" feature (but at least it's usable now).
  • In the current implementation, edges will snap to other edges even when they are "far away" (open seven windows in the sample app, tile them vertically, close the two in the middle, and play, you will see what I mean). This behavior is by design (sic!). If you want to snap only to "close" edges, you can do the following in CMDISnapper::Sizing, when you iterate the child windows:
    • Store a copy of the original rect of the sizing window, inflated by a few pixels (snapWidth comes into mind).
    • Call SnapHLine only when the y coordinate falls into this inflated rect, similar for SnapVLine.
  • Using the "old" and "new" rect in SNAPINFO is a bit useless, as the old coordinates are used only to check whether the window is sized or moved. I originally intended to snap only into the direction of the mouse move, but this resulted in much more complex calculations and stupid behavior.
  • Pressing/releasing Shift or Control requires a minimal mouse move for the display to be updated. Automatically doing so is quite expensive (it requires a keyboard hook or a timer, and Sizing would have to remember the original, unadjusted position).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

peterchen
Klippel
Germany Germany
Peter is tired of being called "Mr. Chen", even so certain individuals insist on it. No, he's not chinese.
 
Peter has seen lots of boxes you youngsters wouldn't even accept as calculators. He is proud of having visited the insides of a 16 Bit Machine.
 
In his spare time he ponders new ways of turning groceries into biohazards, or tries to coax South American officials to add some stamps to his passport.
 
Beyond these trivialities Peter works for Klippel[^], a small german company that wants to make mankind happier by selling them novel loudspeaker measurement equipment.
 

Where are you from?[^]



Please, if you are using one of my articles for anything, just leave me a comment. Seeing that this stuff is actually useful to someone is what keeps me posting and updating them.
Should you happen to not like it, tell me, too

Comments and Discussions

 
QuestionMDI only? Pinmembermikejobu2311-Feb-10 10:10 
AnswerRe: MDI only? Pinmemberpeterchen11-Feb-10 11:58 
GeneralGood Job Pinmemberyasan_v310-Sep-08 22:12 
GeneralNice class PinmemberJaime Stuardo8-Mar-05 12:59 
GeneralNice, indeed PinmemberJörgen Sigvardsson7-Dec-04 9:22 
GeneralNice! PinmemberRavi Bhavnani30-Nov-04 0:04 
GeneralCool PinmemberBrian Delahunty29-Nov-04 22:34 
Cool | :cool:
Thanks peterchen. Updates are useful.
 
Regards,
Brian Dela Smile | :)
 
Now Bloging![^]
Generalapp that makes all your windows snap PinmemberIvan Heckman28-Jun-02 12:04 
GeneralRe: app that makes all your windows snap PinmemberHuyong Zhao16-Aug-07 17:35 
QuestionWhat about pics? PinmemberChristian Rodemeyer14-Oct-01 21:43 
AnswerRe: What about pics? Pinmemberpeterchen15-Oct-01 3:09 
GeneralRe: What about pics? PinmemberChristian Rodemeyer15-Oct-01 8:33 
GeneralNow it's my turn.. Pinmemberpeterchen15-Oct-01 9:47 
GeneralRe: Now it's my turn.. PinmemberChristian Rodemeyer16-Oct-01 8:21 
GeneralRe: Now it's my turn.. Pinmemberpeterchen16-Oct-01 10:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 30 Nov 2004
Article Copyright 2001 by peterchen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid