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?
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.
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.
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.
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.
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:
TabOrder=0
) TabOrder=1
)TabOrder=2
)
TabOrder=0
)TabOrder=1
)TabOrder=3
)To be able to set the tab order of the panel component design time:
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With