Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
Есть TDBLookupComboBox с установленными ListSource, ListField. Делаю так
procedure TForm1.btnTestClick(Sender: TObject);
var
  LDS: TDataSet;
begin
  LDS := DBLookupComboBox1.ListSource.DataSet;
  LDS.DisableControls;
  try
    LDS.Close;
    LDS.Open;
  finally
    LDS.EnableControls;
  end;
end;
после этого работа комбобокса - это черный ящик.

Что происходит: внутри комбобокса (а вернее его родителя TDBLookupControl) есть поле
FListField: TField;
оно заполняется в методе TDBLookupControl.UpdateListFields на основании поля
FListFieldName: string;
Этот метод вызывается из TDataLink на событие датасета
DataSet.DataEvent(deUpdateState, 0);

А теперь проблема - при закрытии датасета для всех полей вызывается
TField.Free
а при открытии поля создаются заново. При этом, т.к. был вызван DisableControls то комбобокс не получает никаких уведомлений и его FListField указывает куда повезет. Если новые поля были созданы по старым адресам - то все будет работать. Если нет - то нет.

Собственно вопрос - как бы минимальными телодвижениями заставить комбобокс переинициализировать свой FListField?

Обнаружено на XE3


С уважением, Vasilisk
28 сен 17, 20:22    [20830139]     Ответить | Цитировать Сообщить модератору
 Re: Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
Валится вот с таким кодом
procedure TForm1.btnTestClick(Sender: TObject);
var
  LDS: TDataSet;
  LList: TObjectList<TField>;
  Li: Integer;
begin
  LList := TObjectList<TField>.Create(True);
  try
    LDS := DBLookupComboBox1.ListSource.DataSet;
    LDS.DisableControls;
    try
      LDS.Close;
      for Li := 0 to 9 do
        LList.Add(TIntegerField.Create(nil));
      LDS.Open;
    finally
      LDS.EnableControls;
    end;
  finally
    LList.Free;
  end;
end;
29 сен 17, 17:06    [20832335]     Ответить | Цитировать Сообщить модератору
 Re: Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
В общем вскрытие показало:

D2007
procedure TDataSet.DataEvent(Event: TDataEvent; Info: NativeInt);
begin
  NotifyDataSources := not (ControlsDisabled or (State = dsBlockRead));
  case Event of
    .............
    deUpdateState:
      if ControlsDisabled then
      begin
        Event := deDisabledStateChange;
        Info := Integer(State <> dsInactive);
        NotifyDataSources := True;
        FEnableEvent := deLayoutChange;
      end;
    ...............
end;

Delphi XE3
procedure TDataSet.DataEvent(Event: TDataEvent; Info: NativeInt);
begin
  NotifyDataSources := not (ControlsDisabled or (State = dsBlockRead));
  case Event of
    .............
    deUpdateState:
      // Send a special event to allow field references to be cleared by data aware controls
      // if a dataset is closed or opened while controls are disabled.
      if ControlsDisabled then
      begin
        IsActive := State <> dsInactive;
        if (IsActive and (FEnableEvent = deLayoutChange)) or         // Opening or
           (not IsActive and (FEnableEvent = deDataSetChange)) then  // Closing
        begin
          Event := deDisabledStateChange;
          NotifyDataSources := True;
          Info := IntPtr(IsActive);
          if IsActive then
            FEnableEvent := deDataSetChange
          else
            FEnableEvent := deLayoutChange;
        end;
      end;
    ...............
end;

Delphi XE7
procedure TDataSet.DataEvent(Event: TDataEvent; Info: NativeInt);
begin
  NotifyDataSources := not (ControlsDisabled or (State = dsBlockRead));
  case Event of
    .............
    deUpdateState:
      // Send a special event to allow field references to be cleared by data aware controls
      // if a dataset is closed or opened while controls are disabled.
      if ControlsDisabled then
      begin
        IsActive := State <> dsInactive;
        if (IsActive and (FEnableEvent = deLayoutChange)) or         // Opening or
           (not IsActive and (FEnableEvent = deDataSetChange)) then  // Closing
        begin
          Event := deDisabledStateChange;
          NotifyDataSources := True;
          Info := IntPtr(IsActive);
          if IsActive and (FEnableEvent <> deLayoutChange) then
            FEnableEvent := deDataSetChange
          else
            FEnableEvent := deLayoutChange;
        end;
      end;
    ...............
end;

Т.е. при вызове EnabledControls во всех версиях проходит событие deLayoutChange, которое DataLink правильно обрабатывает
procedure TListSourceLink.LayoutChanged;
begin
  if FDBLookupControl <> nil then FDBLookupControl.UpdateListFields;
end;
А в ХЕ3 оно затирается событием deDataSetChange, которое обрабатывается так
procedure TListSourceLink.DataSetChanged;
begin
  if FDBLookupControl <> nil then FDBLookupControl.ListLinkDataChanged;
end;


Решение-костыль
type
  {$IFDEF VER240}  // DelphiXE3
  TDBLookupComboBox = class(Vcl.DBCtrls.TDBLookupComboBox)
  protected
    procedure ListLinkDataChanged; override;
  end;
  {$ENDIF}

{ TDBLookupComboBox }

{$IFDEF VER240}
procedure TDBLookupComboBox.ListLinkDataChanged;
begin
  UpdateListFields;
  inherited ListLinkDataChanged;
end;
{$ENDIF}
29 сен 17, 18:09    [20832434]     Ответить | Цитировать Сообщить модератору
 Re: Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
_Vasilisk_
Решение-костыль
Недопроверил. Такой костыль вызывает Stack Overflow. Не хочется переопределять TDataSet.DataEvent. Используется куча разных датасетов
29 сен 17, 18:24    [20832445]     Ответить | Цитировать Сообщить модератору
 Re: Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
Здесь RSP-19036 люди жалуются на Берлин. А ноги растут оттуда же
29 сен 17, 18:49    [20832468]     Ответить | Цитировать Сообщить модератору
 Re: Странности с TDBLookupComboBox  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 9916
В итоге скопировал модуль Data.DB в отдельную папку, поменял строчку
if IsActive then
на
if IsActive and (FEnableEvent <> deLayoutChange) then
и добавил эту папку в Library Path
29 сен 17, 19:14    [20832482]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить