Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom component and tab order

I have a custom component (inheriting from TCustomPanel) that consists of two other components (let's say two edits). How do I get the tab order right when using the component?

In the tab order designer I can only access the component itself which cannot have focus because it is a panel. What happens at runtime is that I can access the edits using the tab key, but only after the two buttons below the component got focused.

How can I change the tab order in this situation?

like image 754
jpfollenius Avatar asked Apr 17 '12 11:04

jpfollenius


People also ask

What determines the tab order?

Control tab order is determined by the order in which controls are drawn on the screen. The first control that you draw is assigned a tab order of 1 , the next is given tab order number 2 , and so on.

What is tab order in accessibility?

The tab order should follow the visual flow of the page: left to right, top to bottom – header first, then main navigation, then page navigation (if present), and finally the footer.

What is tab order in a form?

Tab Order is the order or sequence that the cursor moves from field to field. Initially, the tab order is determined by the order in which the fields are added to the form.

How do I change the order of tabs in Delphi?

The Tab Order dialog facilitates changing the values of the Tab property using the Up and Down arrows instead of explicitly having to specify values for the Tab property in the Object Inspector. Click the Up arrow to move the component up in the tab order, or click the Down arrow to move it down in the tab order.


1 Answers

The tab order of those nested controls within your component is distinct from the tab order of the form on which your component resides. The tab order of the component in the tab order list of the form desides when you tab to the nested controls. Once all tab order lists combined, they result in the final cycle:

  • A control on the form (TabOrder=0)
  • Another control on form (TabOrder=1)
  • Your panel component (TabOrder=2)
    • Edit 1 (TabOrder=0)
    • Edit 2 (TabOrder=1)
  • Another control on form (TabOrder=3)

To be able to set the tab order of the panel component design time:

  • Use the tab order editor in the designer (right click on the parent of the component) and change the tab order with the arrows in the editor, or
  • Publish the TabOrder property for your component and set it in the object inspector:

    type 
      TMyPanel = class(TCustomPanel) 
      published 
        property TabOrder; 
      end; 
    

At runtime it is always possible to set the tab order of the component since the TabOrder property is declared public in TWinControl.

... which cannot have focus because it is a panel.

No, a panel can aqcuire focus just fine, but will not by default. This is handled with the TabStop property, which is False by default. You don't want TabStop to set True for your component since (1) a panel has no indicator it has focus and (2) it is not desired (I imagine).


Changing the tab order of the nested controls is preferably done in the constructor of your component, or at runtime.

To be able to set the tab order of the nested controls within you component at design time requires some more work. I do not think you want that, but since my previous answer (deleted) was rubbish (and voted on, strangly) I have worked out an example as compensation.

First, notice that setting the tab order of those edits with the tab order editor in the designer (right click on the panel component) will change the tab order, but it will not last. That is because those changes are not streamed to the DFM.

To be able to stream/save the design time changes of the controls, you need to publish them:

type
  TMyPanel = class(TCustomPanel)
  private
    FEdit1: TEdit;
    FEdit2: TEdit;
  public
    constructor Create(AOwner: TComponent); override;
  published 
    property Edit1: TEdit read FEdit1;
    property Edit2: TEdit read FEdit2;
  end;

constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEdit1 := TEdit.Create(Self);
  FEdit1.SetBounds(10, 10, 100, 21);
  FEdit1.Name := 'Edit1';
  FEdit1.Parent := Self;
  FEdit1.SetSubComponent(True);
  FEdit2 := TEdit.Create(Self);
  FEdit2.SetBounds(10, 41, 100, 21);
  FEdit2.Name := 'Edit2';
  FEdit2.Parent := Self;
  FEdit2.SetSubComponent(True);
end;

Of course this publishes áll properties of those controls and now users can change whatever they want. To prevent this, consider limiting the published properties of the TEdit controls:

unit MyPanelEdit;

interface

uses
  DesignEditors, Unit2, DesignIntf, SysUtils, Classes, TypInfo, StdCtrls;

type
  TEditProperty = class(TComponentProperty)
  private
    function FilterFunc(const ATestEditor: IProperty): Boolean;
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetProperties(Proc: TGetPropProc); override;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(TEdit), TMyPanel, '', TEditProperty);
end;

{ TEditProperty }

function TEditProperty.FilterFunc(const ATestEditor: IProperty): Boolean;
begin
  Result := ATestEditor.GetName = 'TabOrder';
end;

function TEditProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paSubProperties];
end;

procedure TEditProperty.GetProperties(Proc: TGetPropProc);
var
  LComponents: IDesignerSelections;
  LDesigner: IDesigner;
begin
  LComponents := GetSelections;
  if LComponents <> nil then
  begin
    if not Supports(
        FindRootDesigner(LComponents[0]), IDesigner, LDesigner) then
      LDesigner := Designer;
    GetComponentProperties(LComponents, [tkInteger], LDesigner, Proc,
      FilterFunc);
  end;
end;

end.

This limits the properties of the published TEdit properties to show only TabOrder.

like image 139
NGLN Avatar answered Sep 21 '22 14:09

NGLN