[Transfer] Dragging of MFC borderless windows

Dragging borderless windows in MFC

void CXXXXDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
      PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}

Remember to add message mapping, otherwise this code will not work.

change:

Moving a standard window is achieved by clicking the window title bar with the mouse, but for a window without a title bar, you need to use the mouse to click the area outside the window title bar to move the window. There are two ways to achieve this goal.

method one:

When the window determines the mouse position, Windows sends a WM_NCHITTEST message to the window, which can be processed so that as long as the mouse is within the window, Windows thinks the mouse is on the title bar. This requires overloading the OnNcHitTest function of the CWnd class that handles the WM_NCHITTEST message, and calling the function of the parent class in the function. If HTCLIENT is returned, it means that the mouse is in the client area of the window, so that the overloaded function returns HTCAPTION, making Windows mistakenly think that the mouse is on the title bar. superior.

The following example is actual code using this method:

UINT CEllipseWndDlg::OnNcHitTest(CPoint point)
{
    // Get the window area where the mouse is located
    UINT nHitTest = CDialog::OnNcHitTest(point);
    // If the mouse is in the window client area, return the title bar code to Windows
    // Make Windows click on the title bar for processing, and you can click to move the window
    return (nHitTest==HTCLIENT) ? HTCAPTION : nHitTest;
}

Method Two:

When the user presses the left mouse button in the window client area, Windows is made to think that the mouse is on the title bar, that is, a WM_NCLBUTTONDOWN message whose wParam parameter is HTCAPTION and lParam is the current coordinate is sent in the OnLButtonDown processing function that handles the WM_LBUTTONDOWN message.

Here is the actual code using this method:

void CEllipseWndDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    // Call the parent class processing function to complete basic operations
    CDialog::OnLButtonDown(nFlags, point);
    //Send WM_NCLBUTTONDOWN message
    // Make Windows think the mouse is on the title bar
    PostMessage(WM_NCLBUTTONDOWN,HTCAPTION, MAKELPARAM(point.x, point.y));
 //or SendMessage(WM_SYSCOMMAND,0xF012,0); //0xF012 = SC_MOVE | HTCAPTION
}

First, let’s look at how the system moves program windows under normal circumstances. When the user presses the left mouse button in the title bar area of the program window (not the work area), the following things will happen:

◆ The system sends the WM_NCLBUTTONDOWN message to the window procedure function.

◆ The WM_NCLBUTTONDOWN message will eventually be sent to the DefWindowProc() function in the window procedure function.

◆ The DefWindowProc() function will perform the default action of this message, which is to move the window along with the mouse cursor, based on the left mouse button pressed and moved, as well as the position of the mouse when pressed represented by the HTCAPTION flag.

Let’s test it as an exercise. First, set the following statements in the window callback function (that is, the window procedure function):

caseWM_NCLBUTTONDOWN

     return 0;

Then, I also hold down the left mouse button in the window title bar and move the mouse, but the window does not move with the mouse at this time. What is going on? This is because there is a “return 0” statement in the above statement. This statement caused the WM_NCLBUTTONDOWN message to fail to be passed to the DefWindowProc() function, so it returned early in the window procedure function. Of course, the operation of moving the window was impossible. This also confirms the fact that the final operation of moving the window will be completed by the DefWindowProc() function.

Through the above analysis, we can outline such an operation process: that is, the user presses the left mouse button in the window title bar → the system sends the WM_NCLBUTTONDOWN message → the DefWindowProc() function receives the message → the user moves the mouse → the DefWindowProc() function executes the window along with the mouse Operations that move together.

A conclusion can be drawn from this, that is, in order to realize the operation of moving the window, two conditions must be met: one is to press the left mouse button and move it (the DefWindowProc() function will detect this condition); the other is to press the mouse When left-clicking, the WM_NCLBUTTONDOWN message can be sent and the HTCAPTION flag can be returned.

Based on the above analysis, in the absence of a window title bar, the method of defrauding the DefWindowProc() function is used to move the window entity without a title bar.

1. Actively send WM_NCLBUTTONDOWN message

When the window has no title bar, when the left mouse button is pressed on the window entity, the system will not send the WM_NCLBUTTONDOWN message. This is because the mouse cursor is pressed in the working area of the window. At this time, the system sends is the WM_LBUTTONDOWN message.

However, through the above analysis, we can know that the DefWindowProc() function does not care who sends the WM_NCLBUTTONDOWN message, but only cares whether the message is sent. In this way, as long as we actively send the WM_NCLBUTTONDOWN message in the event of pressing the left mouse button, wouldn’t we be able to satisfy these two conditions at the same time! The code below is designed based on this idea.

case WM_LBUTTONDOWN:
        SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);
        break;

When the message is sent, a message is sent to the DefWindowProc() function through the HTCAPTION parameter, telling it that the left mouse button was pressed on the title bar in the non-working area of the window. Of course, this is false information, but the DefWindowProc() function will believe it to be true and perform corresponding operations based on this information.

2. Actively send WM_SYSCOMMAND messages

case WM_LBUTTONDOWN:
         SendMessage(hWnd,WM_SYSCOMMAND,SC_DRAGMOVE,0);
         break;

The ability to move windows using the WM_SYSCOMMAND message benefits from a newly expanded SC_DRAGMOVE style flag, which literally means “drag and move”. This flag cannot be found in lower versions of compilers (not available in VC 6.0), so it should be declared in advance when referencing this flag:

#define SC_DRAGMOVE 0xF012

You can also use its constant value directly:

SendMessage(hWnd,WM_SYSCOMMAND,0xF012,0);

The above-mentioned mechanism of moving a window without a title bar is similar to that of a window with a title bar. The same thing is that the actual operation is completed by the DefWindowProc() function; the difference is that the way of sending messages is different. One is hidden by the system. Contains sending; the other is sent publicly by the program.

Using the above two methods to move the window has the same visual effect as moving the window with a title bar. That is, when moving, first move an indicator box (a dotted box), and then wait until the new position of the moved form is determined. , when the left mouse button is released, the entity of the window is actually moved to the position pointed by the dotted box. So, can the window entity be moved directly without a dotted box appearing? The answer is yes.

In fact, the operating system has prepared two ways to move windows, one with a dotted frame and the other without a dotted frame, but the default Windows system has a dotted frame. However, if we add the following statement to the above example code:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);

Right now:

case WM_LBUTTONDOWN:
       SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);
       SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);
       break;

This way the dotted indicator box will not appear when moving the window. Note that the position order of the above statements cannot be wrong, otherwise, the dotted box will still appear when moving.

However, the system defaults to moving the window according to the dotted indicator box based on weighing the pros and cons. This is because there is a dotted frame method for moving the window. The window is not actually moved at the beginning. Instead, a wire frame is used to specify the position where the window will reach, and then the window is moved to the specified position all at once (at the specified position). The position is redrawn). In other words, during the process of moving the window, the window entity only needs to be redrawn once. If the dotted frame is not used, but the window entity is moved directly, the window will be drawn N times during the movement of the window, which will increase the burden of graphics processing on the system, seriously reduce the drawing quality of the window, and cause bad visual effects. .

For this reason, in programming practice, it can be arranged as follows: in order to reflect the special-shaped visual effect, the window can be moved without a dotted frame; for a general rectangular window, the window can be moved with a dotted frame. Please ensure the redraw quality of the window.

It is true that the code set above can make the special-shaped window move without a dotted frame, but because the SystemParametersInfo() function is system-level, calling it will affect all program windows on the computer desktop and all program windows will move without a dotted frame. If this is the case if. It will definitely greatly reduce the overall visual effect of the desktop. If you do not want to affect the moving effect of other windows, but just require that the dotted frame does not appear when moving this special-shaped window, you can add the following code to the window procedure function:

case WM_MOUSEMOVE:
                 SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,false,NULL,0);
                 break;

3. Self-compiled code

The first two methods have defects and will change the message flow, making OnLButtonDown, OnLButtonUp, etc. unresponsive.

Self-written code can also realize the moving operation of windows without title bars, and is more flexible and diverse. For example, operations can be performed with the left or right mouse button. The following is the actual code to complete the window movement by right-clicking the mouse. Add the following code statements in the procedure function of the program window:

static POINT pt, pe;

static RECT rt, re;

case WM_RBUTTONDOWN:

    SetCapture(hWnd); //Set mouse capture (to prevent the cursor from running out of the window and losing the mouse hotspot)

    GetCursorPos( & amp;pt); // Get the current position of the mouse cursor pointer

    GetWindowRect(hWnd, & amp;rt); // Get window position and size

    re.right=rt.right-rt.left; // Save window width

    re.bottom=rt.bottom-rt.top; // Save window height

    break;

case WM_RBUTTONUP:

    ReleaseCapture(); // Release mouse capture and return to normal state

    break;

case WM_MOUSEMOVE:

    GetCursorPos( & amp;pe); // Get the new position of the cursor pointer

    if(wParam==MK_RBUTTON) // When the right mouse button is pressed
    {

        re.left=rt.left + (pe.x - pt.x); // New horizontal position of the window

        re.top =rt.top + (pe.y - pt.y); // New vertical position of the window

        MoveWindow(hWnd,re.left,re.top,re.right,re.bottom,true);//Move window

    }

    break;


or:

 

In OnLButtonDown():

SetCapture();

CRect rW;

GetWindowRect(rW);

CPoint ptW = point;

ClientToScreen( & amp;ptW);

m_ptCursorOffset.x = ptW.x - rW.left;

 

In OnMouseMove():

if ((nFlags & amp; MK_LBUTTON) & amp; & amp; this == GetCapture())

{

CPoint ptW = point;

ClientToScreen( & amp;ptW);

ptW.x -= m_ptCursorOffset.x;

ptW.y -= m_ptCursorOffset.y;

::SetWindowPos(m_hWnd, 0,ptW.x,ptW.y,0,0,SWP_NOSIZE);

}

m_ptCursorOffset.y = ptW.y - rW.top;

In OnLButtonUp():

ReleaseCapture();

BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int x, int y,int cx, int cy, UINT nFlags); //MoveWindow can also realize form movement

Function

This function changes the size, position, and Z-order of a child window, pop-up window, or top-level window.

Subwindows, pop-up windows, and top-level windows are divided into
The order of appearance on the screen is sorted, the top-level window is set to the highest level, and is set to the first window in Z order.

Parameters

hWnd:
Window handle.

hWndlnsertAfter: in z order before the set window
Window handle. This parameter must be a window handle, or one of the following values:

HWND_BOTTOM: Place the window at the bottom of the Z order. If the hWnd parameter identifies a top-level window, the window loses its top-level position and is placed at the bottom of other windows.

HWND_NOTOPMOST: Place the window above all non-top-level windows (that is, after all top-level windows). This flag has no effect if the window is already a non-top-level window.

HWND_TOP: Place the window at the top of the Z order.

HWND_TOPMOST: Place the window above all non-top-level windows. The window will remain in top position even if it is not activated.

To see how to use this parameter, see the description section.

x: Specifies the left edge of the window’s new position in client coordinates.

Y: Specifies the top boundary of the window’s new position in client coordinates.

cx: Specifies the new width of the window in pixels.

cy: Specifies the new height of the window in pixels.

uFlags: Window size and positioning flags. This parameter can be a combination of the following values:

SWP_ASYNCWINDOWPOS: If the calling process does not own the window, the system will issue a request to the thread that owns the window. This prevents the calling thread from deadlocking while other threads are processing requests.

SWP_DEFERERASE: Prevents the generation of WM_SYNCPAINT messages.

SWP_DRAWFRAME: Draw a border around the window (defined in
in the window class description).

SWP_FRAMECHANGED: Send the WM_NCCALCSIZE message to the window. This message will be sent even if the window size has not changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window size is changed.

SWP_HIDEWINDOW;Hide the window.

SWP_NOACTIVATE: Do not activate the window. If the flag is not set, the window is activated and set to the top of other top-level windows or non-top-level groups (as set by parameter hWndlnsertAfter).

SWP_NOCOPYBITS: Clear all contents of the client area. If this flag is not set, the effective contents of the client area are saved and copied back to the client area after the window size is updated and repositioned.

SWP_NOMOVE: Maintain the current position (ignoring the X and Y parameters).

SWP_NOOWNERZORDER: Do not change the position of the owner window in z order.

SWP_NOREDRAW: Do not redraw changed content. If this flag is set, no repaint occurs. Applies to all parts of the client area and non-client area (including title bar and scroll bars) and any portion of the parent window exposed due to window movement. If this flag is set,
The application must explicitly invalidate the window and redraw any portion of the window from the portion of the parent window that needs to be redrawn.

SWP_NOREPOSITION; Same as SWP_NOOWNERZORDER flag.

SWP_NOSENDCHANGING: Prevents the window from receiving WM_WINDOWPOSCHANGING messages.

SWP_NOSIZE: Maintain the current size (ignore the cx and Cy parameters).

SWP_NOZORDER: Maintain the current Z order (ignoring the hWndlnsertAfter parameter).

SWP_SHOWWINDOW: Show window.

Return value: If the function succeeds, the return value is non-zero; if the function fails, the return value is zero. If you want to get more error messages, please call the GetLastError function.

Reference: http://baike.baidu.com/view/1080349.htm

Original address: http://blog.sina.com.cn/s/blog_6288219501015dwa.html