Firemonkey中如何让ScrollBox显示在其他组件上方?
Firemonkey中下拉ScrollBox被其他控件遮挡的解决方法
问题描述
我有一个Firemonkey窗体,窗体上包含一个TGridPanelLayout布局(多行单列),其中放置了多个TEdit控件。在顶部的TEdit控件内,我添加了一个下拉按钮和一个嵌套ListView的ScrollBox。点击下拉按钮时,ScrollBox可正常显示,但会被窗体上的其他Edit控件遮挡。我尝试对ScrollBox、ListView甚至TEdit调用BringToFront方法,也调整过ClipParent和ClipChild属性,均未解决问题。请问如何让ScrollBox及其内部的ListView显示在最前方?
补充代码
FMX窗体Unit1代码:
object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 569 ClientWidth = 721 FormFactor.Width = 320 FormFactor.Height = 480 FormFactor.Devices = [Desktop] OnCreate = FormCreate DesignerMasterStyle = 0 object GridPanelLayout1: TGridPanelLayout Align = MostTop Size.Width = 721.000000000000000000 Size.Height = 153.000000000000000000 Size.PlatformDefault = False TabOrder = 0 ColumnCollection = < item Value = 100.000000000000000000 end> ControlCollection = < item Column = 0 Control = Edit1 Row = 0 end> RowCollection = < item Value = 100.000000000000000000 end> object Edit1: TEdit Touch.InteractiveGestures = [LongTap, DoubleTap] Align = Horizontal TabOrder = 2 Position.Y = 48.000000000000000000 Size.Width = 721.000000000000000000 Size.Height = 25.000000000000000000 Size.PlatformDefault = False Caret.Interval = 1 Caret.Width = 10 object DropDownEditButton1: TDropDownEditButton CanFocus = False Cursor = crArrow Size.Width = 28.000000000000000000 Size.Height = 21.000000000000000000 Size.PlatformDefault = False TabOrder = 0 OnClick = DropDownEditButton1Click end object VertScrollBox1: TVertScrollBox Align = Horizontal Position.Y = -16.000000000000000000 Size.Width = 640.000000000000000000 Size.Height = 50.000000000000000000 Size.PlatformDefault = False TabOrder = 0 Visible = False Viewport.Width = 640.000000000000000000 Viewport.Height = 50.000000000000000000 object ListView1: TListView ItemAppearanceClassName = 'TListItemAppearance' ItemEditAppearanceClassName = 'TListItemShowCheckAppearance' HeaderAppearanceClassName = 'TListHeaderObjects' FooterAppearanceClassName = 'TListHeaderObjects' EditMode = True Align = Client Size.Width = 640.000000000000000000 Size.Height = 50.000000000000000000 Size.PlatformDefault = False TabOrder = 0 end end end end object GridPanelLayout2: TGridPanelLayout Align = Client Size.Width = 721.000000000000000000 Size.Height = 416.000000000000000000 Size.PlatformDefault = False TabOrder = 1 ColumnCollection = < item Value = 100.000000000000000000 end> ControlCollection = < item Column = 0 Control = Edit2 Row = 0 end item Column = 0 Control = Edit3 Row = 1 end item Column = 0 Control = Edit4 Row = 2 end item Column = 0 Control = Edit5 Row = 3 end> RowCollection = < item SizeStyle = Absolute Value = 50.000000000000000000 end item SizeStyle = Absolute Value = 50.000000000000000000 end item SizeStyle = Absolute Value = 50.000000000000000000 end item SizeStyle = Absolute Value = 50.000000000000000000 end> object Edit2: TEdit Touch.InteractiveGestures = [LongTap, DoubleTap] Align = Center TabOrder = 5 Size.Width = 721.000000000000000000 Size.Height = 32.000000000000000000 Size.PlatformDefault = False end object Edit3: TEdit Touch.InteractiveGestures = [LongTap, DoubleTap] Align = Center TabOrder = 4 Size.Width = 721.000000000000000000 Size.Height = 31.000000000000000000 Size.PlatformDefault = False end object Edit4: TEdit Touch.InteractiveGestures = [LongTap, DoubleTap] Align = Center TabOrder = 3 Size.Width = 721.000000000000000000 Size.Height = 28.000000000000000000 Size.PlatformDefault = False end object Edit5: TEdit Touch.InteractiveGestures = [LongTap, DoubleTap] Align = Center TabOrder = 2 Size.Width = 721.000000000000000000 Size.Height = 26.000000000000000000 Size.PlatformDefault = False end end end
创建及按钮点击事件代码:
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, FMX.StdCtrls, FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base, FMX.ListView; type TForm1 = class(TForm) GridPanelLayout1: TGridPanelLayout; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; DropDownEditButton1: TDropDownEditButton; VertScrollBox1: TVertScrollBox; ListView1: TListView; GridPanelLayout2: TGridPanelLayout; procedure DropDownEditButton1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.DropDownEditButton1Click(Sender: TObject); begin Edit1.BringToFront; VertScrollBox1.Position.Y := TSpeedButton(Sender).Height; VertScrollBox1.Height := (GridPanelLayout1.Height + GridPanelLayout2.Height) - Edit1.LocalToAbsolute(PointF(0, 0)).Y - Edit1.Height - 10; VertScrollBox1.Visible := true; end; procedure TForm1.FormCreate(Sender: TObject); begin ListView1.Items.Clear; for var I := 0 to 100 do ListView1.Items.Add.Text := 'Demo ' + I.ToString; end; end.
问题表现

解决方案
问题核心是Firemonkey的控件绘制层级由父容器决定,ScrollBox嵌套在Edit1内部时,层级再高也无法突破父容器和同级控件的遮挡。以下是两种可行解决方法:
方法一:将ScrollBox挂载到窗体顶层
- 在设计器中把
VertScrollBox1从Edit1的子控件中移出,直接设为Form1的直接子控件。 - 修改按钮点击事件代码,重新设置ScrollBox的位置和大小:
procedure TForm1.DropDownEditButton1Click(Sender: TObject); var EditAbsPos: TPointF; begin // 获取Edit1在窗体中的绝对位置 EditAbsPos := Edit1.LocalToAbsolute(PointF(0, 0)); // 设置ScrollBox位置为Edit1下方 VertScrollBox1.Position.X := EditAbsPos.X; VertScrollBox1.Position.Y := EditAbsPos.Y + Edit1.Height; // 设置宽度和高度 VertScrollBox1.Width := Edit1.Width; VertScrollBox1.Height := ClientHeight - EditAbsPos.Y - Edit1.Height - 10; // 置顶显示 VertScrollBox1.BringToFront; VertScrollBox1.Visible := True; end;
- 确保
VertScrollBox1的ClipParent属性设为False,避免被窗体边界裁剪。
方法二:用无边框窗体作为下拉弹窗(更优雅)
- 创建新的
TForm(如TListPopupForm),设置BorderStyle为bsNone,FormStyle为fsStayOnTop。 - 在该窗体中添加
TListView并铺满整个窗体。 - 修改按钮点击事件,计算位置后显示弹窗:
procedure TForm1.DropDownEditButton1Click(Sender: TObject); var EditAbsPos: TPointF; PopupForm: TListPopupForm; begin EditAbsPos := Edit1.LocalToAbsolute(PointF(0, 0)); PopupForm := TListPopupForm.Create(nil); try PopupForm.Left := Screen.Left + Round(EditAbsPos.X); PopupForm.Top := Screen.Top + Round(EditAbsPos.Y + Edit1.Height); PopupForm.Width := Round(Edit1.Width); PopupForm.Height := ClientHeight - Round(EditAbsPos.Y) - Edit1.Height - 10; // 填充ListView数据 PopupForm.ListView1.Items.Clear; for var I := 0 to 100 do PopupForm.ListView1.Items.Add.Text := 'Demo ' + I.ToString; PopupForm.ShowModal; // 可选:将选中项文本赋值给Edit1 if PopupForm.ListView1.Selected <> nil then Edit1.Text := PopupForm.ListView1.Selected.Text; finally PopupForm.Free; end; end;
原方法无效原因
BringToFront()仅能在当前父容器内调整层级,无法突破父容器范围。ClipParent/ClipChild控制的是控件是否被父容器裁剪,与层级显示无关。
内容的提问来源于stack exchange,提问作者fisi-pjm
相关产品推荐
相关产品推荐

