Delphi Delphi XE 应用程序中的 DBCombobox,仅在 .Items 中显示值而不是实际的不合格数据字段值

Delphi DBCombobox in Delphi XE app, only shows values if in .Items and not the actual nonconforming Datafield value

我在使用 ADO 访问的 master/detail 应用程序中遇到了一个非常奇怪的 DBComboBox 问题。如果您有一个包含项目列表的 DBComboBox (.Style=csDropDown),并且您输入了一些列表中不存在的文本,则 table 的 DBComboBox 字段中的值在返回时不会出现到那个记录。我已使用下面的 DBNavigator.OnClick 代码尝试解决此问题,但只有当 table 中的第一条记录包含不在列表中的值时它才有效。当您将第一条记录中的 DBComboBox 的值更改为列表中的值时,DBComboBox 文本中将不会出现不一致的项目。有人找到解决办法了吗?

procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);
var
    SavePlace : TBookmark;
begin
  if (DBComboBox1.Text='') then begin
    SavePlace := TADODataSet(DBNavigator1.DataSource.DataSet).GetBookmark;
    TADODataSet(DBNavigator1.DataSource.DataSet).Requery;
    TADODataSet(DBNavigator1.DataSource.DataSet).GotoBookMark(SavePlace);
    TADODataSet(DBNavigator1.DataSource.DataSet).FreeBookMark(SavePlace);
  end;
end;

不幸的是我没有安装XE,但我做了一个示例项目 在 D7 和西雅图重现您的问题。代码如下所示,我认为 你会发现,如果你按照下面的确切步骤,它表明有一些东西 相当奇怪的事情。 更新 请参阅答案的底部以获得可能的 work-around,我认为这比您在问题中引用的代码更可取。

如您所见,除了 Form1 本身,所有组件都是在 运行 时间创建的 完全在代码中。这是为了消除任何怀疑行为是否引起的 通过一些晦涩的 属性 设置(不是),如果你想提交它 作为错误报告提交给 EMBA。出于类似的原因,我使用了 TClientDataSet 这样 该应用程序不依赖于任何外部数据。

步骤(第一次尝试时请完全按照步骤 4-7

  1. 重新启动IDE并创建一个新项目并编辑主窗体的.Pas文件,如下所示。重新启动 IDE 的原因是我发现如果它已经 运行ning 了很长时间(在我的情况下是两天),则不当行为的详细信息 应用程序略有变化)。

  2. 编译并运行.

  3. 应用将从所选 DBGrid 中的第一个开始。

  4. 在 DBComboBox 中输入任何内容('X' 即可),然后单击“保存”工具按钮 在 DBNavigator 上。

  5. 只单击 DBNavigator 上的下一步 (>) 工具按钮一次。 DBComboBox 现在显示 'Two'.

  6. 只单击 DBNavigator 上的优先 (<) 工具按钮一次。 DBComboBox 现在是空的。

  7. 只单击 DBNavigator 上的优先 (<) 工具按钮一次。 DBComboBox 现在显示 您在第 4 步中输入的内容。

  8. 关闭应用程序。 IDE 调试器很可能会捕获错误并打开 CPU window。 此故障发生在线路

    DestroyWindow(FHandle);

在 TApplication.Destroy。我不是 Windows 内部专家,但我认为这很可能是因为在第 6 步中导致空白结果的任何原因导致了一些损坏。事实是 第 7 步导致 DBComboBox 正确显示您键入的内容让我怀疑原因实际上是 在 DBComboBox 与其 FieldDataLink 交互的方式中,FieldDataLink 将它连接到数据集。

顺便说一句,如果您在 TForm1 的 FormDestroy 中调用 DBComboBox1.Free,则不会发生错误 在我看来,可以确认故障与导致您的问题的原因有关。

所有这一切,以及它在 Delphi 的 25 年中显然已被忽视的事实,似乎很奇怪 大部头书。这个演示应用程序可以显示另一个在 DBGrid 中潜伏了类似时间的怪癖。查看 它:

  1. 注释掉所有对 DBComboBox 的引用并在网格选项中恢复 dgMultiSelect 设置它们的线。编译并 运行 应用程序。

  2. 单击第一行“名称”列中的单元格,键入内容并保存。

  3. 单击“下一步”工具按钮一次。第一行没有 de-select 本身。 AFAICT(通过在表单标题上显示 DBGrid 的书签计数)这不是 因为它在第一行保存了一个书签。

在我写这篇文章的时候,我想到了一个可能的 work-around,我会更新它 如果我能让它工作的话,这包括在内。

代码

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender : TObject);
  private
    procedure SetUpDataSet;
    procedure SetUpGUI;
  protected
  public
    ClientDataSet1 : TClientDataSet;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    DBNavigator1: TDBNavigator;
    DBComboBox1: TDBComboBox;
  end;

[...]

procedure TForm1.SetUpGUI;
begin

  ClientDataset1 := TClientDataSet.Create(Self);

  DataSource1 := TDataSource.Create(Self);
  DataSource1.DataSet := ClientDataSet1;

  DBGrid1 := TDBGrid.Create(Self);
  DBGrid1.Top := 8;
  DBGrid1.Left := 8;
  DBGrid1.Width := 425;
  DBGrid1.Options := [dgEditing, dgTitles, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit{, dgMultiSelect}];
  DBGrid1.DataSource := DataSource1;
  DBGrid1.Parent := Self;

  DBNavigator1 := TDBNavigator.Create(Self);
  DBNavigator1.DataSource := DataSource1;
  DBNavigator1.Top := 144;
  DBNavigator1.Left := 16;
  DBNavigator1.Parent := Self;

  DBComboBox1 := TDBComboBox.Create(Self);
  DBComboBox1.DataField := 'Name';
  DBComboBox1.DataSource := DataSource1;
  DBComboBox1.Top := 240;
  DBComboBox1.Left := 16;
  DBComboBox1.Parent := Self;
end;

procedure TForm1.SetUpDataSet;
var
  Field : TField;
begin

  //  Create 2 fields in the CDS

  Field := TIntegerField.Create(Self);
  Field.FieldName := 'ID';
  Field.FieldKind := fkData;
  Field.DataSet := ClientDataSet1;

  Field := TStringField.Create(Self);
  Field.FieldName := 'Name';
  Field.Size := 40;
  Field.FieldKind := fkData;
  Field.DataSet := ClientDataSet1;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetUpGUI;
  SetUpDataSet;

  //  Set up DBComboBox

  DBComboBox1.Style := csDropDown;
  DBComboBox1.Items.Add('One');
  DBComboBox1.Items.Add('Two');
  DBComboBox1.Items.Add('Three');

  //  Next, set up the CDS
  ClientDataSet1.CreateDataSet;

  ClientDataSet1.InsertRecord([1, '']);
  ClientDataSet1.InsertRecord([2, 'Two']);
  ClientDataSet1.InsertRecord([3, '']);

  ClientDataSet1.First;

end;

可以work-around在Form1中添加如下方法:

procedure TForm1.ClientDataSet1AfterScroll(DataSet: TDataSet);
var
  S : String;
begin
  S := DataSet.FieldByName('Name').AsString;
  if S <> DbComboBox1.Text then
    DbComboBox1.Text := S;
  Caption := IntToStr(DBGrid1.SelectedRows.Count);

end;

然后,在 SetUpGUI 方法中,在创建 ClientDataSet1 的行之后立即添加以下内容:

ClientDataset1.AfterScroll := ClientDataSet1AfterScroll;

我没有对此进行彻底的测试,但它似乎在我上面描述的步骤的测试条件下工作。