1
votes

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

5

5 Answers

0
votes

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:

enter image description here

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.

2
votes

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)

1
votes

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.

1
votes

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.

0
votes

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