You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++Builder FMX窗体缩放问题求助:基于TPanel实现标准缩放机制

Delphi/C++Builder 透明窗体缩放问题修复

问题说明

主窗体TForm frmMain启用透明属性(Transparent = true),通过内部的TPanel PanelRoot(设置Align = Client、Padding = 6)实现窗体缩放功能。现有代码存在两个问题:

  • 无法快速缩放:鼠标快速移动时会停止缩放
  • 光标与缩放状态异常:鼠标移出窗口后返回,光标仍保持缩放样式,且无需按下左键就会触发窗体缩放

原代码

//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormCreate(TObject* Sender)
{
    IsResizingHorz              = false;
    IsResizingVert              = false;
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseDown(TObject* Sender, TMouseButton Button, TShiftState Shift, float X, float Y)
{
    if (Button == TMouseButton::mbLeft) {
        bool IsNearRightEdge  = (X <= 4 || X >= Width - 4);
        bool IsNearBottomEdge = (Y <= 4 || Y >= Height - 4);

        if (IsNearRightEdge) {
            IsResizingHorz = true;
            StartX         = X;
            InitialWidth   = Width;
        } else if (IsNearBottomEdge) {
            IsResizingVert = true;
            StartY         = Y;
            InitialHeight  = Height;
        }
    }
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseUp(TObject* Sender, TMouseButton Button, TShiftState Shift, float X, float Y)
{
    if (Button == TMouseButton::mbLeft) {
        IsResizingHorz    = false;
        IsResizingVert    = false;
        PanelRoot->Cursor = crDefault;
    }
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseMove(TObject* Sender, TShiftState Shift, float X, float Y)
{
    if (X <= 4 || X >= Width - 4) {
        PanelRoot->Cursor = crSizeWE;
    } else if (Y <= 4 || Y >= Height - 4) {
        PanelRoot->Cursor = crSizeNS;
    } else
        PanelRoot->Cursor = crDefault;

    if (IsResizingHorz) {
        auto NewWidth = InitialWidth + (X - StartX);
        SetBounds(Left, Top, NewWidth, Height);
    } else if (IsResizingVert) {
        auto NewHeight = InitialHeight + (Y - StartY);
        SetBounds(Left, Top, Width, NewHeight);
    }
}
//---------------------------------------------------------------------------

问题原因分析

  1. 快速缩放失效:鼠标快速移动时会移出PanelRoot范围,导致MouseMove事件不再触发,缩放动作中断。
  2. 状态与光标异常:
    • 鼠标在窗口外释放左键时,PanelRoot的MouseUp事件不会触发,导致IsResizingHorz/IsResizingVert一直保持true状态,返回窗口后会自动触发缩放。
    • 鼠标移出窗口时没有重置光标,且缩放过程中MouseMove仍会修改光标样式,导致光标状态混乱。

修复后的代码

//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormCreate(TObject* Sender)
{
    IsResizingHorz = false;
    IsResizingVert = false;
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseDown(TObject* Sender, TMouseButton Button, TShiftState Shift, float X, float Y)
{
    if (Button != TMouseButton::mbLeft) return;

    bool IsNearRightEdge = (X >= PanelRoot->Width - 4);
    bool IsNearBottomEdge = (Y >= PanelRoot->Height - 4);

    if (IsNearRightEdge) {
        IsResizingHorz = true;
        StartX = X;
        InitialWidth = Width;
        SetCapture(PanelRoot->Handle); // 捕获鼠标,确保移出控件仍能收到事件
    } else if (IsNearBottomEdge) {
        IsResizingVert = true;
        StartY = Y;
        InitialHeight = Height;
        SetCapture(PanelRoot->Handle);
    }
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseUp(TObject* Sender, TMouseButton Button, TShiftState Shift, float X, float Y)
{
    if (Button != TMouseButton::mbLeft) return;

    IsResizingHorz = false;
    IsResizingVert = false;
    PanelRoot->Cursor = crDefault;
    ReleaseCapture(); // 释放鼠标捕获
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::PanelRootMouseMove(TObject* Sender, TShiftState Shift, float X, float Y)
{
    // 缩放过程中不修改光标
    if (IsResizingHorz || IsResizingVert) {
        if (IsResizingHorz) {
            // 限制最小宽度避免异常
            int NewWidth = InitialWidth + (X - StartX);
            if (NewWidth < 200) NewWidth = 200;
            SetBounds(Left, Top, NewWidth, Height);
        } else if (IsResizingVert) {
            // 限制最小高度避免异常
            int NewHeight = InitialHeight + (Y - StartY);
            if (NewHeight < 150) NewHeight = 150;
            SetBounds(Left, Top, Width, NewHeight);
        }
        return;
    }

    // 非缩放状态下更新光标
    bool nearRight = (X >= PanelRoot->Width - 4);
    bool nearBottom = (Y >= PanelRoot->Height - 4);
    if (nearRight) {
        PanelRoot->Cursor = crSizeWE;
    } else if (nearBottom) {
        PanelRoot->Cursor = crSizeNS;
    } else {
        PanelRoot->Cursor = crDefault;
    }
}
//---------------------------------------------------------------------------
// 添加PanelRoot的MouseLeave事件处理
void __fastcall TfrmMain::PanelRootMouseLeave(TObject* Sender)
{
    // 鼠标移出控件时重置光标,若意外未释放缩放状态则强制重置
    if (!IsResizingHorz && !IsResizingVert) {
        PanelRoot->Cursor = crDefault;
    }
}
//---------------------------------------------------------------------------
// 可选:添加窗体的MouseUp事件处理,防止鼠标在窗口外释放导致状态残留
void __fastcall TfrmMain::FormMouseUp(TObject* Sender, TMouseButton Button, TShiftState Shift, float X, float Y)
{
    if (Button == TMouseButton::mbLeft) {
        IsResizingHorz = false;
        IsResizingVert = false;
        PanelRoot->Cursor = crDefault;
        ReleaseCapture();
    }
}
//---------------------------------------------------------------------------

修复要点说明

  • 鼠标捕获机制:在MouseDown时调用SetCapture,确保鼠标移出控件或窗口时仍能收到MouseMove和MouseUp事件,解决快速缩放失效问题。
  • 状态与光标管理:
    • 缩放过程中跳过光标更新逻辑,避免光标被错误修改。
    • 添加MouseLeave事件,在鼠标移出控件时重置光标。
    • 添加窗体的MouseUp事件作为兜底,防止鼠标在窗口外释放导致缩放状态残留。
  • 边界限制:添加最小宽高限制,避免窗体缩放过小导致异常。
  • 边缘检测优化:改为仅检测右边缘和下边缘(若需要支持左/上边缘缩放,可单独扩展逻辑),避免边缘检测冲突。

内容的提问来源于stack exchange,提问作者antarey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:59:53