Delphi DBCombobox в Delphi приложении XE, показывает только значения, если в .Items, а не фактическое несоответствующее значение поля данных - PullRequest
1 голос
/ 01 апреля 2020

Я столкнулся с очень странной проблемой DBComboBox в приложении master / detail, использующем Access через ADO. Если у вас есть DBComboBox (.Style = csDropDown), содержащий список элементов, и вы вводите некоторый текст, которого нет в списке, значение в поле DBComboBox таблицы не появится при переходе к этой записи. Я использовал приведенный ниже код DBNavigator.OnClick, чтобы попытаться решить эту проблему, но он работает, только если первая запись в таблице содержит значение, отсутствующее в списке. Когда вы изменяете значение 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;

1 Ответ

2 голосов
/ 03 апреля 2020

К сожалению, я не установил XE, но я сделал пример проекта, который воспроизводит вашу проблему в D7 и Сиэтле. Код показан ниже, и я думаю, что вы обнаружите, что если вы выполните точные шаги ниже, это показывает, что происходит нечто довольно странное. Обновление См. Нижнюю часть ответа для возможного обходного пути, который, я думаю, предпочтительнее кода, который вы цитируете в своей q.

Как вы увидите, за исключением самой формы Form1 Все компоненты создаются во время выполнения полностью в коде. Это должно устранить любые сомнения относительно того, вызвано ли поведение каким-либо неясным параметром свойства (это не так), и в случае, если вы sh отправите его в EMBA в качестве отчета об ошибке. По той же причине я использовал TClientDataSet, чтобы приложение не зависело от каких-либо внешних данных.

Шаги (пожалуйста, следуйте шагам 4-7 точно при первой их попытке)

  1. Перезапустите IDE, создайте новый проект и отредактируйте файл .Pas для главной формы, как показано ниже. Причина перезапуска IDE состоит в том, что я обнаружил, что, если он работал в течение длительного времени (два дня в моем случае), сведения о неправильном поведении приложения изменяются незначительно).

  2. Скомпилируйте и запустите.

  3. Приложение запустится с первым из выбранной в DBGrid.

  4. Введите что-нибудь (будет «X» do) в DBComboBox, затем нажмите кнопку «Сохранить» на DBNavigator.

  5. Нажмите кнопку «Далее» (>) на DBNavigator только один раз. DBComboBox теперь отображает «Два».

  6. Нажмите кнопку «Приор» (<) на DBNavigator только один раз. DBComboBox теперь пуст. </p>

  7. Нажмите кнопку «Prior» (<) на DBNavigator только один раз. DBComboBox теперь отображает то, что вы ввели на шаге 4. </p>

  8. Закройте приложение. Скорее всего, отладчик IDE обнаружит ошибку и откроет окно процессора. Эта ошибка возникает в строке

    DestroyWindow (FHandle);

в TApplication.Destroy. Я не эксперт по внутренним ресурсам Windows, но я думаю, что это вероятно из-за некоторого искажения, вызванного тем, что вызывает пустой результат в шаге 6. Тот факт, что шаг 7 заставляет DBComboBox правильно отображать то, что вы ввели, заставляет меня подозревать, что причина в том, как DBComboBox взаимодействует со своим FieldDataLink, который соединяет его с набором данных.

Кстати, тот факт, что ошибка не возникает, если вы вызываете DBComboBox1.Free в FormDestroy TForm1, мне кажется, чтобы подтвердить, что ошибка связана с тем, что вызывает вашу проблему.

Все это, и тот факт, что он, очевидно, прошел незамеченным за 25 лет Delphi, мне кажется очень странным. Это демонстрационное приложение может показать еще одну причуду, которая скрывалась в DBGrid в течение подобного времени. Чтобы увидеть это:

Закомментируйте все ссылки на DBComboBox и восстановите dgMultiSelect среди параметров сетки в строке, которая их устанавливает. Скомпилируйте и запустите приложение.

Нажмите в ячейке столбца Имя для первой строки, введите что-нибудь и сохраните его.

Нажмите Следующая кнопка инструментов один раз. Первая строка не отменяет выбор себя, как следует. AFAICT (отображая количество закладок DBGrid в заголовке формы), это не потому, что он сохранил закладку в первой строке.

Пока я писал это, возможно мне пришло в голову обходное решение, которое я обновлю и включу, если смогу заставить его работать.

код

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;

возможный обход Добавьте следующий метод в форму 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;

Я не проверял это полностью, но, кажется, работает в условиях тестирования шагов, которые я описал выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...