Перемещающиеся столбцы в DBGRID, кажется, перемещают прикрепленные поля набора данных

StackOverflow https://stackoverflow.com/questions/1983274

Вопрос

На прошлой неделе я заметил что -то, чего не ожидал, и опишу ниже. Мне любопытно, почему это происходит. Это что -то внутреннее для класса Tdataset, артефакт TDBGrid или что -то еще?

Порядок полей в открытом клиенте изменился. В частности, я создал ClientDataSet в коде, вызывая CaneleatatSet после определения его структуры с использованием FieldDefs. Первым полем в структуре этой клиента DATASET было поле даты с именем Startofweek. Всего несколько минут спустя код, который я также написал, который предположил, что поле Startofweek находилось в позиции Nuereeth, ClientDataset.fields [0], не удалось, поскольку поле Startofweek больше не было первым полем в ClientDataset.

После некоторого исследования я узнал, что вполне возможно, что каждая область в ClientDataset может, в определенный момент, в какой -то позиции отличается от исходной структуры в то время, когда был создан ClientDataset. Я не знал, что это может произойти, и поиск в Google также не упомянул об этом эффекте.

То, что случилось, не было волшебством. Поля не изменили позицию сами по себе, и они не изменились, основываясь на том, что я делал в своем коде. То, что заставило поля физически изменить позицию в ClientDataSet, так это то, что пользователь изменил порядок столбцов в DBGRID, к которому был прикреплен клиент -датасет (конечно, через компонент данных). Я повторил этот эффект в Delphi 7, Delphi 2007 и Delphi 2010.

Я создал очень простое приложение Delphi, которое демонстрирует этот эффект. Он состоит из одной формы с одним DBGRID, источником данных, двумя клиентами и двумя кнопками. Обработчик событий Oncreate в этой форме выглядит следующим образом

procedure TForm1.FormCreate(Sender: TObject);
begin
  with ClientDataSet1.FieldDefs do
  begin
    Clear;
    Add('StartOfWeek', ftDate);
    Add('Label', ftString, 30);
    Add('Count', ftInteger);
    Add('Active', ftBoolean);
  end;
  ClientDataSet1.CreateDataSet;
end;

Button1, которая помечена, Show ClientDataset Structure, содержит следующий обработчик событий OnClick.

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name);
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
      sl.Add(ClientDataSet1.Fields[i].FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Чтобы продемонстрировать эффект движения поля, запустите это приложение и нажмите кнопку с надписью Show ClientDataset Structure. Вы должны увидеть что -то подобное здесь:

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Затем перетащите столбцы DBGRID, чтобы переоценить порядок отображения полей. Нажмите кнопку Show ClientDataset Structure еще раз. На этот раз вы увидите что -то похожее на то, что здесь показано:

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
Label
StartOfWeek
Active
Count

Что примечательно в этом примере, так это то, что столбцы DBGRID перемещаются, но существует очевидное влияние на положение полей в ClientDataset, так что поле, которое было в позиции ClientDataset.field [0] на одном Точка не обязательно есть через несколько мгновений. И, к сожалению, это не явно проблема клиента. Я выполнил тот же тест с Ttables на основе BDE и Ado Adotables и получил тот же эффект.

Если вам никогда не нужно ссылаться на поля в вашем клиенте, отображаемых в DBGRID, вам не нужно беспокоиться об этом эффекте. Для всех вас я могу вспомнить несколько решений.

Самым простым, хотя и не необходимым, предпочтительным способом избежать этой проблемы является предотвращение повторного заказа поля в DBGRID. Это можно сделать, удалив флаг DgresizeColumn из свойства Options DBGRID. Хотя этот подход эффективен, он устраняет потенциально ценную опцию отображения, с точки зрения пользователя. Кроме того, удаление этого флага не только ограничивает повторное порядок столбцов, но и препятствует изменению размера столбцов. (Чтобы узнать, как ограничить переупорядочение столбца без удаления опции изменения размера столбца, см. http://delphi.about.com/od/adptips2005/a/bltip0105_2.htm.)

Второй обходной путь состоит в том, чтобы избежать ссылки на поля набора данных на основе их буквальной позиции (поскольку это сущность проблемы). В порядке, если вам нужно обратиться к полю подсчета, не используйте DataSet.Fields [2]. Пока вы знаете название поля, вы можете использовать что -то вроде dataSet.fieldbyName ('count').

Однако есть один довольно большой недостаток в использовании FieldbyName. В частности, этот метод идентифицирует поле, итерация через свойство поля набора данных, ищет совпадение на основе имени поля. Поскольку это происходит каждый раз, когда вы называете FieldByName, это метод, которого следует избегать в ситуациях, когда на поле нужно упоминать много раз, например, в цикле, который навигает на большой набор данных.

Если вам нужно неоднократно ссылаться на поле (и большое количество раз), рассмотрите возможность использования чего -то вроде следующего фрагмента кода:

var
  CountField: TIntegerField;
  Sum: Integer;
begin
  Sum := 0;
  CountField := TIntegerField(ClientDataSet1.FieldByName('Count'));
  ClientDataSet1.DisableControls;  //assuming we're attached to a DBGrid
  try
    ClientDataSet1.First;
    while not ClientDataSet1.EOF do
    begin
      Sum := Sum + CountField.AsInteger;
      ClientDataSet1.Next;
    end;
  finally
    ClientDataSet1.EnableControls;
  end;

Существует третье решение, но оно доступно только тогда, когда ваш набор данных является ClientDataset, как в моем первоначальном примере. В этих ситуациях вы можете создать клон оригинального ClientDataset, и он будет иметь исходную структуру. В результате, какое бы поле было создано в позиции нулевого уровня, все еще будет находиться в этой позиции, независимо от того, что пользователь сделал с DBGRID, который отображает данные ClientDatasets.

Это продемонстрировано в следующем коде, который связан с обработчиком событий OnClick кнопки с надписью «Показ клонированной структуры клиентской даты».

procedure TForm1.Button2Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
  CloneClientDataSet: TClientDataSet;
begin
  CloneClientDataSet := TClientDataSet.Create(nil);
  try
    CloneClientDataSet.CloneCursor(ClientDataSet1, True);
    sl := TStringList.Create;
    try
      sl.Add('The Structure of ' + CloneClientDataSet.Name);
      sl.Add('- - - - - - - - - - - - - - - - - ');
      for i := 0 to CloneClientDataSet.FieldCount - 1 do
        sl.Add(CloneClientDataSet.Fields[i].FieldName);
      ShowMessage(sl.Text);
    finally
      sl.Free;
    end;
  finally
    CloneClientDataSet.Free;
  end;
end;

Если вы запустите этот проект и нажмете кнопку с надписью «Показать клонированную структуру клиентской даты», вы всегда получите истинную структуру ClientDataset, как показано здесь

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Приложение:

Важно отметить, что фактическая структура базовых данных не затронута. В частности, если после изменения порядка столбцов в DBGRID вы называете Savetofile Method of ClientDataset, сохраненная структура является исходной (истинной внутренней) структурой. Кроме того, если вы копируете свойство данных одного ClientDataSet другому, то Destination ClientDataset также показывает истинную структуру (что аналогично эффекту, наблюдаемому при клонировании исходного клиента).

Аналогичным образом, изменения в порядках столбцов DBGRID, связанных с другими тестируемыми наборами данных, включая Ttable и Adotable, фактически не влияют на структуру базовых таблиц. Например, TTABLE, который отображает данные из таблицы Paradox Paradox Custment.DB, которая поставляется с Delphi, фактически не изменяет структуру этой таблицы (и вы не ожидаете этого).

Из этих наблюдений мы можем сделать вывод, что внутренняя структура самого набора данных остается нетронутой. В результате я должен предположить, что где -то есть вторичное представление структуры набора данных. И он должен быть либо связан с набором данных (который может быть излишним, поскольку не все использование набора данных требуют этого), связанного с DBGRID (что имеет больше смысла, поскольку DBGRID использует эту функцию, но не является Поддерживается наблюдением, что переупорядочение TFILD, по -видимому, сохраняется с самим набором данных) или является чем -то другим.

Другая альтернатива заключается в том, что эффект связан с tgriddatalink, который является классом, который дает многоуштание элементы управления (например, DBGRID) их осведомленность данных. Тем не менее, я склонен отвергнуть это объяснение, поскольку этот класс связан с сетью, а не с набором данных, поскольку эффект кажется сохраняющимся с самими классами данных.

Что возвращает меня к первоначальному вопросу. Является ли этот эффект чем -то внутренним для класса Tdataset, артефактом TDBGrid или что -то еще?

Позвольте мне также подчеркнуть здесь что -то, что я добавил в один из приведенных ниже комментариев. Больше всего на свете мой пост предназначен для того, чтобы разработчики осознавали, что когда они используют DBGRID, чьи заказы столбцов могут быть изменены, что порядок их TFIELDS также может измениться. Этот артефакт может вводить прерывистые и серьезные ошибки, которые могут быть очень трудно определить и исправить. И нет, я не думаю, что это ошибка Delphi. Я подозреваю, что все работает так, как было разработано, чтобы работать. Просто многие из нас не знали, что это поведение происходило. Теперь мы знаем.

Это было полезно?

Решение

Очевидно, поведение по дизайну. На самом деле это не связано с DBGRID. Это всего лишь побочный эффект, устанавливающего индекс поля. Например, это утверждение,

ClientDataset1.fields [0] .index: = 1;

Приведет к тому, что вывод кнопки «Показать структуру ClientDataset» соответствующим образом изменится, либо есть сетка, либо нет. Документация для TFILD.INDEX CATS;

«Измените порядок позиции поля в наборе данных путем изменения значения индекса. Изменение значения индекса влияет на порядок, в котором поля отображаются в сетях данных, но не на положение полей в таблицах физической базы данных».

Следует сделать вывод, что обратное также должно быть истинным, и изменение порядка полей в сетке должно привести к изменению полевых индексов.


Код, вызывающий это в tcolumn.setindex. Tcustomdbgrid.columnmoved устанавливает новый индекс для перемещенного столбца, а Tcolumn.setindex устанавливает новый индекс для поля этого столбца.

procedure TColumn.SetIndex(Value: Integer);
[...]
        if (Col <> nil) then
        begin
          Fld := Col.Field;
          if Assigned(Fld) then
            Field.Index := Fld.Index;
        end;
[...]

Другие советы

Wodzu опубликовал решение проблемы переупорядоченного поля, которая была специфична для набора данных ADO, но он привел меня к решению, которое схоже и доступно для всех наборов данных (независимо от того, правильно ли он реализован в все Наборы данных - это еще одна проблема). Обратите внимание, что ни этот ответ, ни Wodzu's на самом деле не являются ответом на исходный вопрос. Вместо этого это решение отмеченной проблемы, тогда как вопрос относится к тому, где происходит этот артефакт.

Решение, к которому решение Wodzu приводило меня, была FieldbyNumber, и это метод свойства полей. Есть два интересных аспекта использования FieldbyNumber. Во -первых, вы должны квалифицировать его ссылку со свойством Fields вашего набора данных. Во-вторых, в отличие от массива полей, который принимает индексатор на основе нуля, FieldbyNumber-это метод, который требует одного параметра для обозначения позиции THFILE, на который вы хотите ссылаться.

Ниже приведена обновленная версия обработчика событий Button1, которую я разместил в своем оригинальном вопросе. Эта версия использует FieldByNumber.

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name +
      ' using FieldByNumber');
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
      sl.Add(ClientDataSet1.Fields.FieldByNumber(i + 1).FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Для образца проекта этот код дает следующий выход, независимо от ориентации столбцов в связанном DBGRID:

The Structure of ClientDataSet1 using FieldByNumber
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Чтобы повторить, обратите внимание, что ссылка на базовый TFFIED требует, чтобы FieldByNumber была квалифицирована со ссылкой на поля. Кроме того, параметр для этого метода должен лежать в диапазоне DataSet.FieldCount. В результате, чтобы ссылаться на первое поле в наборе данных, вы используете следующий код:

ClientDataSet1.Fields.FieldByNumber(1)

Как и в массиве полей, Fieldbynumber возвращает ссылку на TFILD. В результате, если вы хотите ссылаться на метод, который является конкретным для конкретного класса TFILD, вы должны поднять возвращенное значение в соответствующий класс. Например, чтобы сохранить содержимое Tblobfield в файл, вам, возможно, придется сделать что -то вроде следующего кода:

TBlobField(MyDataSet.Fields.FieldByNumber(6)).SaveToFile('c:\mypic.jpg');

Обратите внимание, что я не предполагаю, что вы должны ссылаться на Tfields в наборе данных, используя целочисленные литералы. Лично использование переменной TFILD, которая инициатизируется с помощью одноразового вызова в FieldByName, более читаемо и невосприимчиво к изменениям в физическом порядке структуры таблицы (хотя и не застраховано с изменениями в именах ваших полей!).

Однако, если у вас есть наборы данных, связанные с DBGRID, столбцы которых могут быть переупорядочены, и вы ссылаетесь на поля этих наборов данных, используя целочисленные литералы в качестве индексеров массива полей, вы можете рассмотреть вопрос Анкет

Кэри, я думаю, я нашел решение для этой проблемы. Вместо использования полей обертки VCL нам необходимо использовать свойство внутренних полей объекта CORCTSet COM.

Вот как на это следует ссылаться:

qry.Recordset.Fields.Item[0].Value

На эти поля не влияют поведение, которое вы описали ранее. Таким образом, мы все еще можем ссылаться на поля по их индексу.

Проверьте это и скажите мне, что было результатом. Это сработало для меня.

Редактировать:

Конечно, это будет работать только для компонентов ADO, а не для TclientDataset ...

Edit2:

Кэри, я не знаю, является ли это ответом на ваш вопрос, однако я подталкивал людей на форумах Embarcadero, и Уэйн Ниддери дал мне довольно подробный ответ обо всех этих движениях поля.

Короче говоря: Если вы явно определяете свои столбцы в TDBGRID, индексы полевых веществ не движутся! Есть немного больше смысла, не так ли?

Читайте полную ветку здесь:https://forums.embarcadero.com/post!reply.jspa?messageid=197287

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top