C++Builder: Create a TForm with BorderStyle bsNone

2019-01-27 04:11发布

问题:

I'd like to have a TForm with BorderStyle = bsNone (no borders, no caption), which is nevertheless resizable and movable. I already figured out how to do the resizable part, but I'm stuck with getting it movable.

/**
*   Overrides standard CreateParams method to create a TForm with BorderStyle
*    bsNone but is nevertheless movable and resizable
**/
void __fastcall CreateParams(TCreateParams &Params)
{
    BorderStyle = bsNone;
    TForm::CreateParams(Params);
    //set flag WS_EX_STATICEDGE
    //for more details on this flag, see http://msdn.microsoft.com/en-us/library/ms632680(v=vs.85).aspx
    Params.ExStyle = Params.ExStyle ^ 0x00020000L;
    //set flag WS_SIZEBOX
    //for more details on this flag, see http://msdn.microsoft.com/en-us/library/ff700543(v=VS.85).aspx
    Params.Style = Params.Style ^ 0x00040000L;
}

It's probably only a matter of finding the correct flags. Any ideas?

回答1:

The best way to allow a form to move is to mimic how a form moves when you click and drag the title bar. Since your window doesn't have a title bar, when Windows needs to know what part of your form the mouse cursor is over you lie and tell Windows that it is indeed over the title bar. After that, moving works normally since the default behaviour kicks in.

To do this, you respond to the WM_NCHITTEST message, which is easily done by overriding the form's WndProc method. This message is sent in several situations (not just mouse clicks or movement) so don't assume anything about what the user's doing when you get this message. Handle it by setting the message result to HTCAPTION, a value indicating the position is over the title bar.

Important things to note are:

  • This method will be called for every message the form gets; don't do anything slow or complex here.
  • Always let the default inherited implementation of WndProc handle the message. This is essential for most messages, since you only want to change the behaviour for this one and if you don't call the inherited implantation nothing will happen for the messages at all, but it's important for this one too since you don't know what code needs to be sent this message. I.e., you want to add to how your program responds to this, not replace it. This is a general good guideline for all message processing you intercept / add. The WndProc documentation mentions this too.
  • You can set a region of the form to be the draggable bit by checking the mouse coordinates. In the below code, only the top 100 pixels of the form will be draggable.

Sample code:

void __fastcall TForm1::WndProc(Messages::TMessage& Message) {
    TForm::WndProc(Message); // Inherited implementation
    if (Message.Msg == WM_NCHITTEST) {
        TWMNCHitTest& oMsg = reinterpret_cast<TWMNCHitTest&>(Message);
        TPoint oPoint(oMsg.XPos, oMsg.YPos); // Screen coordinates
        oPoint = ScreenToClient(oPoint); // Now in form-local coordinates
        // It's in the title bar (caption) if it's in a rectangle at the top of the form
        if ((oPoint.x > 0 && oPoint.x < ClientWidth) &&
            (oPoint.y > 0 && oPoint.y < 100))
        {
            oMsg.Result = HTCAPTION;
        }
    }
}


回答2:

Try to place this code to Form OnMouseDown event handler:

ReleaseCapture();
this->Perform(WM_SYSCOMMAND, 0xF012, 0);