正确的方法来改变TEdits Delphi Xe5的重点

时间:2014-01-18 18:47:10

标签: delphi focus firemonkey delphi-xe5

我一直在搜索,一般的答案似乎已经放置了

SomeEdit2.setFocus;
在SomeEdit1.OnExit事件中

。我试过这个(使用Delphi Xe5,为iOS开发),它会导致应用程序崩溃。该应用程序不会抛出错误,它只是空白和崩溃。我已尝试将相同的代码放在其他事件中,但它不能按预期工作。例如,当放置在SomeEdit1.OnChange事件中时,当用户在虚拟键盘上点击“完成”时 - 焦点切换到所需的控件,但键盘不显示并停止正常工作。

当用户点击虚拟键盘上提供的“完成”按钮时,在控件之间更改焦点的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

您无法将VCL-Control行为与FMX-Control行为进行比较,因为有时它们表现不同 - 它们不应该,但它们不同。

在VCL中,您有一个OnExit事件,它在焦点离开控件后立即发生。所以这是一个OnAfterExit事件。

在FMX中,OnExit事件在焦点消失之前触发。所以这是OnBeforeExit

procedure TControl.DoExit;
begin
  if FIsFocused then
  begin
    try
      if CanFocus and Assigned(FOnExit) then
        FOnExit(Self);
      FIsFocused := False;

现在,这与您当前的问题有什么关系?

如果您将焦点设置为OnExit事件中的另一个控件,则会调用当前活动控件DoExit方法,该方法会调用OnExit事件,并且您有一个完美的圆圈。

所以你有几个选项来解决这个问题

错误报告

最佳解决方案是创建错误报告并让emba修复此错误。

已经有错误报告117752出于同样的原因。所以我发布了解决方案作为评论。

补丁FMX.Controls.pas

FMX.Controls复制到项目源目录中并修补错误代码(只需一行)

procedure TControl.DoExit;
begin
  if FIsFocused then
  begin
    try
      FIsFocused := False; // thats the place to be, before firering OnExit event 
      if CanFocus and Assigned(FOnExit) then
        FOnExit(Self);
      //FIsFocused := False; <-- buggy here

SetFocus来控制

要在OnExit中设置焦点,您需要做更多工作,因为将焦点更改为下一个控件的消息已经排队。您必须确保焦点更改为所需控件,>已经排队的焦点更改消息。最简单的方法是使用计时器。

以下是一个示例FMX表单,其中包含3个编辑控件,每个表单都有OnExit个事件

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.Edit;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    EnsureActiveControl_Timer: TTimer;
    procedure EnsureActiveControl_TimerTimer(Sender: TObject);
    procedure Edit1Exit(Sender: TObject);
    procedure Edit2Exit(Sender: TObject);
    procedure Edit3Exit(Sender: TObject);
  private
    // locks the NextActiveControl property to prevent changes while performing the timer event 
    FTimerSwitchInProgress: Boolean;
    FNextActiveControl: TControl;
    procedure SetNextActiveControl(const Value: TControl);
  protected
    property NextActiveControl: TControl read FNextActiveControl write SetNextActiveControl;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Edit1Exit(Sender: TObject);
begin
  NextActiveControl := Edit3;
end;

procedure TForm1.Edit2Exit(Sender: TObject);
begin
  NextActiveControl := Edit1;
end;

procedure TForm1.Edit3Exit(Sender: TObject);
begin
  NextActiveControl := Edit2;
end;

procedure TForm1.EnsureActiveControl_TimerTimer(Sender: TObject);
begin
  EnsureActiveControl_Timer.Enabled := False;
  FTimerSwitchInProgress := True;
  try
    if (Self.ActiveControl <> NextActiveControl) and NextActiveControl.CanFocus then
      NextActiveControl.SetFocus;
  finally
    FTimerSwitchInProgress := False;
  end;
end;

procedure TForm1.SetNextActiveControl(const Value: TControl);
begin
  if FTimerSwitchInProgress 
  or (FNextActiveControl = Value) 
  or (Assigned(Value) and not Value.CanFocus) 
  or (Self.ActiveControl = Value) 
  then
    Exit;

  FNextActiveControl := Value;
  EnsureActiveControl_Timer.Enabled := Assigned(FNextActiveControl);
end;

end.