Reputation: 33
I'm writing a Firemonkey HD program in Delphi XE5 Update 2 for windows platform. I have a tabcontrol with a tabitem on the form and some edit boxes in the tabitem. I have the taborder set accordingly (0,1,2,..), when I enter Edit1 and hit tab, Edit1 looses focus but Edit2 does not get focus.
I tried putting the editboxes on the main form and in a panel and there they work correctly, When I'm in edit1 and hit tab it goes to edit2 and so on, but on a tabitem in a tabcontrol it does not.
Does anyone know of a work around for this or maybe a setting that I missed?
Thanks for any help
Upvotes: 1
Views: 4380
Reputation: 11
I solved this in a very simple way. I changed the height of the form to 2000 and inserted the fields in my scrollbox. Then I returned the form's height to the original. From what I understand, firemonkey gets lost when the component is outside the visible area of the scrollbox. But if the form has a larger height, the scrollbox thinks everything is visible and works.
Upvotes: 1
Reputation: 511
Here is an actual fix for the problem, which I have also described in http://vldgeorgiev.wordpress.com/2014/04/01/delphi-tab-key-and-taborder-not-working
It lies in the source of the TTabItem in the FMX.TabControl.pas
unit.
There is an overridden method called TTabItem.DoAddObject
procedure TTabItem.DoAddObject(const AObject: TFmxObject);
var
ControlTmp: TControl;
begin
if Assigned(FContent) and not AObject.Equals(FContent) and not AObject.Equals(ResourceLink) then
begin
FContent.AddObject(AObject);
...
end;
while it should be
procedure TTabItem.DoAddObject(const AObject: TFmxObject);
var
ControlTmp: TControl;
begin
if Assigned(FContent) and not AObject.Equals(FContent) and not AObject.Equals(ResourceLink) then
begin
FContent.AddObject(AObject);
AddToTabList(AObject); // This line is missing in the original source
...
end;
The problem is that when a form's KeyDown
method processes the Tab key it calls the AdvanceTabFocus
method, which checks the FTabList
for any child components. Because the TTabItem's original DoAddObject
method was missing a line it never added the child controls to that list so the AdvanceTabFocus method can not find the next control. Instead it sets the focus to the first control on the form.
To use this fix either copy the modified FMX.TabControl.pas unit next to your project files, or compile the DCUs and place them in the Lib... subfolders of your Delphi install folder. If you don't have the sources you are out of luck.
Btw, setting the TabOrder numbers is not always enough. You have to right-click and use "Tab order..." or even reorder the controls manually in the forms text (by using Alt-F12)
Upvotes: 2
Reputation: 1
I had the same problem and found an easy workaround: Just make sure that the order of creation is your tab order. You can do that by editing the (textual) form file (.fmx). (by pressing F12 in the form design mode) For example: If you have a form with just 3 edit controls the for look like this:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 154
ClientWidth = 215
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [dkDesktop, dkiPhone, dkiPad]
DesignerMobile = False
DesignerWidth = 0
DesignerHeight = 0
DesignerDeviceName = ''
DesignerOrientation = 0
DesignerOSVersion = ''
object Edit1: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 0
Position.X = 40.000000000000000000
Position.Y = 88.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
object Edit2: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 1
Position.X = 40.000000000000000000
Position.Y = 56.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
object Edit3: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 2
Position.X = 40.000000000000000000
Position.Y = 24.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
end
The tab order is Edit1:, Edit2:, Edit3:
If you change it to :
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 154
ClientWidth = 215
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [dkDesktop, dkiPhone, dkiPad]
DesignerMobile = False
DesignerWidth = 0
DesignerHeight = 0
DesignerDeviceName = ''
DesignerOrientation = 0
DesignerOSVersion = ''
object Edit1: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 0
Position.X = 40.000000000000000000
Position.Y = 88.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
object Edit3: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 2
Position.X = 40.000000000000000000
Position.Y = 24.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
object Edit2: TEdit
Touch.InteractiveGestures = [igLongTap, igDoubleTap]
TabOrder = 1
Position.X = 40.000000000000000000
Position.Y = 56.000000000000000000
Width = 100.000000000000000000
Height = 22.000000000000000000
KillFocusByReturn = False
end
end
than the tab order is Edit1:, Edit3:, Edit2: no matter what the value of the TabOrder property is
Upvotes: 0
Reputation: 849
I had the same issue in my application, so I wrote a workaround:
http://andydunkel.net/delphi/coding/2013/11/23/firemonkey_xe_4_taborder_workaround.html
I am using a FormHelper class, the key down method of the Form (reacts to the Tab Key) and the help context property of the controls.
First I was setting the help context, you can use the tag for example too. But I am already using the tag property for something else:
unit FormHelper;
interface
type
TFormHelper = class helper for TForm
procedure DoTabHandlingXE(comp : TForm; tabOrder : Integer);
end;
implementation
//Workaround for Tab-Bug in Firemonkey
procedure TFormHelper.DoTabHandlingXE(comp: TForm; tabOrder : Integer);
var
i, c :integer;
current : TComponent;
currentNext : integer;
focus : TStyledControl;
begin
c := Self.ComponentCount - 1;
currentNext := 9999;
focus := nil;
for i := 0 to c do begin
current := Self.Components[i];
if (current is TStyledControl) then begin
if ((current as TStyledControl).HelpContext < currentNext) and
((current as TStyledControl).HelpContext > tabOrder) then begin
currentNext := (current as TStyledControl).HelpContext;
end;
end;
end;
for i := 0 to c do begin
current := Self.Components[i];
if (current is TStyledControl) then begin
if (currentNext = (current as TStyledControl).HelpContext) then begin
focus := (current as TStyledControl);
end;
end;
end;
if focus <> nil then begin
focus.SetFocus;
end;
end;
end.
Of course the code does nothing to far, since the method is not called yet. So the next step is to implement the KeyDown event in the form:
procedure TfrmEinzField.KeyDown(var Key: Word; var KeyChar: Char;
Shift: TShiftState);
var
control : TStyledControl;
begin
if Key = vkTab then
begin
//custom handling
if (Self.GetFocused is TStyledControl) then begin
control := (Self.GetFocused as TStyledControl);
DoTabHandlingXE(Self, control.HelpContext);
end;
end else
inherited; //do default handling
end;
In the code, we get the currently focused control. Then we call the method we wrote before with the current Form variable and the controls HelpContext value. With that workaround the tab key is now working as expected, jumping to the next control.
More details are in the blog post.
Upvotes: 0
Reputation: 1112
This is a known bug: http://qc.embarcadero.com/wc/qcmain.aspx?d=117380
Looks like it might be fixed for XE6.
You can manually set focus using Control.SetFocus
, but then you have to set that up yourself for each control. You could setup the OnKeyUp
event and see if they pressed tab (VK_TAB
or 9), and if they did set focus to your next control.
Upvotes: 1