📄 0,1410,26370,00.html
字号:
<HTML>
<HEAD>
<TITLE>How to Make Your Form "Snap" to the Screen Edge or Another Form</TITLE>
</HEAD>
<BODY MARGINWIDTH="5" MARGINHEIGHT="5" TOPMARGIN="0" LEFTMARGIN="0" BGCOLOR="#FFFFFF">
<A NAME="top"></A>
<table>
<TR>
<td>
<P>
<SPAN CLASS="title3">How to Make Your Form "Snap" to the Screen Edge or Another Form</SPAN>
- by Borland Developer Support Staff
<BLOCKQUOTE CLASS="abstract">
<p><B>Abstract:</B>This document details the steps necessary to create "snapping" or "magnetic" windows, much like in Winamp.</p>
</BLOCKQUOTE>
</table>
<a name="top"></a>
<table width="100%" border=0 cellpadding=5 cellspacing=0>
<tr><td valign=center align=middle>
<font color="white" face="Arial Black">
C</font><font face="Arial Black">reating "Magnetic" Windows that Snap To Things</font></td></tr>
<tr><td align=left>
<p>
<big>T</big>his question has been posed many times in various newsgroups: How do I make my form "snap" to things (screen edge/other windows, etc). Winamp can do it, so how do I? This is actually not a very difficult thing to acheive. It requires adding a couple of message-handlers to your form and that's about it.
</p>
<table cellpadding="0" cellspacing="0" border="1">
<tr><td align=middle>Table Of Contents</td></tr>
<tr><td><A style="text-decoration: none;" href="#objective" onMouseover="window.status='Objective';return true" onMouseOut="window.status='';return true;">Objective</a></td></tr>
<tr><td><A style="text-decoration: none;" href="#msg" onMouseover="window.status='Snapping Window';return true" onMouseOut="window.status='';return true;">Creating the Message Handlers</a></td></tr>
<tr><td><A style="text-decoration: none;" href="#code" onMouseover="window.status='Code';return true" onMouseOut="window.status='';return true;">Code</a></td></tr>
</table>
</td></tr>
<tr><td align = left>
<a name="objective"></a><A class=loginLink href="#top"><font face="Arial" color="#000000">Objective</font></a>
</td></tr>
<tr><td>
In this article, I will explain how to create some message handlers for your form so that it will be able to "snap" to the screen edge or any other window you specify. This document assumes that you have a grasp of the following concepts:<br><br>
<ul>
<li>Borland C++Builder</li>
<li>Some Win32 API</li>
<li>Understanding of Win32 Messaging system</li>
<li>VCL Experience</li>
</ul><br>
</td></tr>
<tr><td align = left>
<a name="msg"></a><A class=loginLink href="#top"><font face="Arial">Making your form snap-able</font></a>
</td></tr>
<tr><td>
<p>
<big>T</big>here is no built-in feature of the Win32 API that allows windows to snap to each other, so we will have to add this functionality ourselves. To do this, we add two message handlers to our form. The first one, <code>WM_SETTINGCHANGE</code>. This one updates our snap area if the user changes the size of the screen, etc. The brunt of the work will be handled by <code>WM_WINDOWPOSCHANGING</code>. This message is sent whenever
the window is moved or resized or focused, but all we really care about is when it's moved.<br><br></p>
<p>To set a message handler for your form, you use the MESSAGE_MAP macros:<br>
<code><pre>
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(<message>,<msg struct type>,<handler>);
MESSAGE_HANDLER(...);
....
END_MESSAGE_MAP(TForm);
</pre></code>
For the <code>WM_SETTINGCHANGE</code> message, we have several things:
First, a private RECT structure (work_area) that tells us what our current work area is. We'll update this with an
<code>UpdateWorkArea</code> function. <code>UpdateWorkArea</code> will be called from our message handler<br><br>
Here is how it will look (you can see how it all fits together below in the code section):<br><br>
<table border=1 cellpadding=3 cellspacing=0 bgcolor=#cfcfcf>
<tr><td>
<font color="#0000AA"><i>//in our header file</i></font><br>
<code>MESSAGE_HANDLER(WM_SETTINGCHANGE,TMessage,WMSettingChanged);</code><br>
<font color="#0000AA"><i>//---------------------------------------------------------------------------</i></font>
<pre><code><b>void</b> <b>__fastcall</b> Tsnapform::WMSettingChanged(TMessage &msg)
{
UpdateWorkArea();
}
<font color="#0000AA"><i>//---------------------------------------------------------------------------</i></font>
<b>void</b> <b>__fastcall</b> Tsnapform::UpdateWorkArea()
{
SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
<font color="#0000AA"><i>//get the size of the desktop or whatever</i></font>
}</code></pre>
</td></tr>
</table>
<br>
<p>Now we will set about making our <code>WM_WINDOWPOSCHANGING</code> handler. This will do what is necessary to have a nice snap-to window; you'll even be able to tell it how far to snap from.</p>
<p>As above, we tell our form about our message handler, then put it in a function. In this case, the function will be <code>WMWindowPosChanging</code>. The code is listed below, but with the comments it should be relatively straight forward.</p>
<p><code>WM_WINDOWPOSCHANGING</code> sends us a <code>WINDOWPOS</code> structure through the lParam parameter. This structure tells us where our window is going and how big it's going to be. We can change this info through the <code>WINDOWPOS</code> structure. This is how we get it to snap.
</p>
<p>There are a couple of private class members used in this function:<br>
snapped (<b>bool</b>) tells it if it's snapped or not<br>
snapwin (<b>HWND</b>) the window to snap to<br>
thresh (<b>int</b>) is the number of pixels at which to snap<br>
msg.WindowPos (<b>WINDOWPOS</b>) is the <code>WINDOWPOS</code> structure from lParam</p>
<p>
<table bgcolor=#cfcfcf border=1 cellpadding=3 cellspacing=0>
<tr><td>
<pre><code>
<b>void</b> <b>__fastcall</b> Tsnapform::WMWindowPosChanging(TWMWindowPosChanging &msg)
{
RECT sr; <font color="#0000AA"><i>//rect to snap to</i></font>
snapped=<b>false</b>;
<font color="#0000AA"><i>//test window</i></font>
<b>if</b> (snapwin && IsWindowVisible(snapwin)) <font color="#0000AA"><i>//if we should snap to another window</i></font>
{
<b>if</b> (GetWindowRect(snapwin,&sr)) <font color="#0000AA"><i>//get it's bounds</i></font>
{
<font color="#0000AA"><i>//if we're within the snap threshold then snap</i></font>
<b>if</b> ( (msg.WindowPos->x <= (sr.right+thresh)) &&
(msg.WindowPos->x >= (sr.right-thresh)) ) {
<b>if</b> ((msg.WindowPos->y > sr.top) && (msg.WindowPos->y < sr.bottom)) {
<font color="#0000AA"><i>//disallow "open air" snaps</i></font>
snapped=true;
msg.WindowPos->x = sr.right;
}
}
<b>else</b> <b>if</b> ((msg.WindowPos->x + msg.WindowPos->cx) >= (sr.left-thresh) &&
(msg.WindowPos->x + msg.WindowPos->cx) <= (sr.left+thresh)) {
<b>if</b> ((msg.WindowPos->y > sr.top) && (msg.WindowPos->y < sr.bottom)) {
snapped=true;
msg.WindowPos->x = sr.left-msg.WindowPos->cx;
}
}
<b>if</b> ( (msg.WindowPos->y <= (sr.bottom+thresh)) &&
(msg.WindowPos->y >= (sr.bottom-thresh)) ) {
<b>if</b> ((msg.WindowPos->x > sr.left) && (msg.WindowPos->x < sr.right)) {
snapped=true;
msg.WindowPos->y = sr.bottom;
}
}
<b>else</b> <b>if</b> ((msg.WindowPos->y + msg.WindowPos->cy) <= (sr.top+thresh) &&
(msg.WindowPos->y + msg.WindowPos->cy) >= (sr.top-thresh)) {
<b>if</b> ((msg.WindowPos->x > sr.left) && (msg.WindowPos->x < sr.right)) {
snapped=true;
msg.WindowPos->y = sr.top-msg.WindowPos->cy;
}
}
}
}
<font color="#0000AA"><i>//test screen</i></font>
sr = work_area;
<font color="#0000AA"><i>//we have to test this in reverse so we switch the testing of the rect</i></font>
<b>if</b> (abs(msg.WindowPos->x) <= (sr.left+thresh)) {
snapped=true;
msg.WindowPos->x = sr.left;
}
<b>else</b> <b>if</b> ((msg.WindowPos->x + msg.WindowPos->cx) >= (sr.right-thresh) &&
(msg.WindowPos->x + msg.WindowPos->cx) <= (sr.right+thresh)) {
snapped=true;
msg.WindowPos->x = sr.right-msg.WindowPos->cx;
}
<b>if</b> (abs(msg.WindowPos->y) <= (sr.top+thresh)) {
snapped=true;
msg.WindowPos->y = sr.top;
}
<b>else</b> <b>if</b> ((msg.WindowPos->y+msg.WindowPos->cy) >= (sr.bottom-thresh) &&
(msg.WindowPos->y+msg.WindowPos->cy) <= (sr.bottom+thresh)) {
snapped=true;
msg.WindowPos->y = sr.bottom-msg.WindowPos->cy;
}
}
</code></pre>
</td></tr>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -