Действие набора данных Delphi по умолчанию Tdatasetdelete

Одна из замечательных вещей Delphi - это список TActionlist. Еще лучше использовать стандартные TAations, такие как TDataset-actions. У меня есть одна форма с несколькими простыми таблицами. Поэтому я позволил Delphi решить, какой источник данных / таблица активен с несколькими TDatasetinsert/delete/edit и т. Д.

Но теперь я хочу, чтобы действие удаления имело диалоговое окно "Вы уверены" или что-то в этом роде. Если я вмешиваюсь в событие execute действия, действие, кажется, останавливается после диалога. Поэтому я хочу выполнить действие удаления самостоятельно, например somedatasource.dataset.delete. Но я не могу понять, какой источник данных активен для этого TDatasetdelete.

TDatasetdelete имеет свойство источника данных, но по умолчанию оно равно nil, и чтение его дает нарушение прав доступа. Даже если я оставлю его неназначенным, строка данных будет удалена из одного из моих источников данных при выполнении TDatasetdelete. В этих обстоятельствах, как мне узнать, какой источник данных является "активным", другими словами, какой источник данных он будет использовать при выполнении.

1 ответ

Обновление: я думаю, что теперь я понимаю, что вы на самом деле спрашиваете, что даже если вы оставите источник данных действия DataSetDelete неназначенным, как получается, что действию каким-то образом удается "узнать", с каким источником данных работать?

Для этого есть объяснение, если какой-либо из ваших источников данных подключен к TDBGrids или любому другому компоненту, поддерживающему DB, чья связь данных содержит код, подобный следующему:

function TCustomDBGrid.UpdateAction(Action: TBasicAction): Boolean;
begin
  Result := (DataLink <> nil) and DataLink.UpdateAction(Action);
end;

Если вы установите точку останова на Result := ... вы обнаружите, что он вызывается несколько раз во время работы приложения.

Теперь попробуйте это с моим кодом ниже:

  1. Отключите две DBGrids от их соответствующего источника данных, затем скомпилируйте и запустите:

    Результат: пункт меню DataSetDelete отключен (!).

  2. Затем подключите DBGrid2 к DataSource2. Скомпилируйте и запустите.

    Результат: пункт меню DataSetDelete включен, и при щелчке по нему текущая строка удаляется из CDS2.

  3. Затем подключите DBGrid1 к DataSource1. Скомпилируйте и запустите.

    Результат: пункт меню DataSetDelete включен, и при щелчке по нему текущая строка удаляется из CDS1.

Как вы можете видеть, если ваш код явно не устанавливает свойство DataSetAction для DataSetAction, это действие работает с источником данных первого канала данных, который возвращает True из компонента, поддерживающего DB. UpdateAction функция.

Другими словами, дело не в том, что действие DataSetDelete "знает", какой источник данных использовать, если оставить свойство DataSource действия DataSetDelete неназначенным, а в том, что ссылки на данные компонентов с поддержкой БД сообщают действию, какой источник данных активен.

Обновление 2 Чтобы понять, что я имею в виду, удалите любой обработчик, который у вас есть на данный момент, для OnExecute объекта DataSetDelete. Затем установите точку останова на TDataSetDelete.ExecuteTarget в DBActns.Pas. Когда он отключается, посмотрите на стек вызовов, и вы обнаружите, что он вызывается из TCustomDBGrid.ExecuteAction, поэтому идентификатор набора данных передается в действие DataSetDelete, поэтому я думаю, что нет никакого способа выяснить, идентификатор набора данных из самого действия DataSetDelete.

Тем не менее, есть простой способ обойти это. Присоедините следующий обработчик BeforeDelete к каждому из ваших наборов данных:

procedure TCDSForm.CDS1BeforeDelete(DataSet: TDataSet);
begin
  if MessageDlg(DataSet.Name + ': Delete record?', mtConfirmation, [mbYes, mbNo], 0) <>  mrYes then
    Abort;
end;

Затем, независимо от того, пытаетесь ли вы удалить запись набора данных, используя действие DataSetDelete или нет, будет вызван этот обработчик события, и если пользователь не отвечает "Да", вызывается процедура Abort, вызывающая исключение без вывода сообщений, которое предотвращает исключение из судебного разбирательства.

==============

Оригинальный ответ следует. Я приберу это позже

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

Мой вопрос: как я могу прочитать активное имя источника данных компонента

Ответ заключается в том, что действие TDataSetDelete имеет свойство DataSource, и это просто вопрос установки того, к какому источнику данных вы хотите быть активным.

"Delphi знает, какой источник данных активен" Нет, конечно, нет, как это возможно, пока вы не скажете ему, с каким источником данных вы хотите, чтобы действие DataSetDelete работало? И способ, которым вы делаете это, чтобы установить его DataSource имущество. Чтобы следовать тому, что я имею в виду, скомпилируйте код ниже, установите точку останова на

Caption := 'Execute'

внутри DataSetDelete1Execute, затем скомпилируйте и запустите проект и выберите пункт меню DataSetDelete1Execute. Когда срабатывает точка останова, оцените `TDataSetDelete(Sender).DataSource. Вы обнаружите, что значение равно Nil, потому что вы (еще) не указали действие, источником данных которого должно быть выполнено действие.

Затем раскомментируйте строку

SetDataSource(DataSource1);

в FormCreate и повторите оценку. Теперь действие знает, потому что вы сказали ему, какой источник данных он должен считать активным.

Если вы пропустили это в первый раз, это линия

  DatasetDelete1.DataSource := FDataSource;

в procedure TCDSForm.SetDataSource(const Value: TDataSource) который устанавливает набор данных, который использует действие DatasetDelete1.

Кстати, в этом нет "магии" - посмотрите на источник Delphi

function TDataSetAction.GetDataSet(Target: TObject): TDataSet;
begin
  { We could cast Target as a TDataSource since HandlesTarget "should" be
    called before ExecuteTarget and UpdateTarget, however, we're being safe. }
  Result := (Target as TDataSource).DataSet;
end;

а также

procedure TDataSetDelete.ExecuteTarget(Target: TObject);
begin
  GetDataSet(Target).Delete;
end;

Код (Обновлено):

unit cdsActionListu;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, DBGrids, DB, DBClient, StdCtrls, ExtCtrls, DBCtrls, ActnList,
  DBActns, Menus;

type

  TCDSForm = class(TForm)
    CDS1: TClientDataSet;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    DBGrid2: TDBGrid;
    CDS2: TClientDataSet;
    DataSource2: TDataSource;
    DBNavigator2: TDBNavigator;
    ActionList1: TActionList;
    DataSetDelete1: TDataSetDelete;
    MainMenu1: TMainMenu;
    actSelectDataSource: TAction;
    ListBox1: TListBox;
    DataSetDelete11: TMenuItem;
    procedure DataSetDelete1Execute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    FDataSource: TDataSource;
    procedure SetDataSource(const Value: TDataSource);
  public
    property ActiveDataSource : TDataSource read FDataSource write SetDataSource;
  end;

var
  CDSForm: TCDSForm;

implementation

{$R *.DFM}


function CreateField(AFieldClass : TFieldClass; AOwner : TComponent; ADataSet : TDataSet;
AFieldName, AName : String; ASize : Integer; AFieldKind : TFieldKind) : TField;
begin
  Result := AFieldClass.Create(AOwner);
  Result.FieldKind := AFieldKind;
  Result.FieldName := AFieldName;
  Result.Name := AName;
  Result.Size := ASize;
  Result.DataSet := ADataSet;
end;

procedure TCDSForm.DataSetDelete1Execute(Sender: TObject);
begin
  Caption := 'Execute';
  { uncomment the following to actually delete the record
      if MessageDlg('Delete record?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then begin
          TDataSetDelete(Sender).DataSource.DataSet.Delete;
        end;
  }

end;

procedure TCDSForm.FormCreate(Sender: TObject);
var
  Field : TField;
begin
  Field := CreateField(TIntegerField, Self, CDS1, 'ID', 'CDS1ID', 0, fkData);
  Field := CreateField(TStringField, Self, CDS1, 'StringField', 'CDS1Stringfield', 40, fkData);

  CDS1.CreateDataSet;
  CDS1.InsertRecord([1, 'CDS1 Value1']);
  CDS1.InsertRecord([2, 'CDS1 Value2']);

  Field := CreateField(TIntegerField, Self, CDS2, 'ID', 'CDS2ID', 0, fkData);
  Field := CreateField(TStringField, Self, CDS2, 'StringField', 'CDS2Stringfield', 40, fkData);

  CDS2.CreateDataSet;
  CDS2.InsertRecord([1, 'CDS2 Value1']);
  CDS2.InsertRecord([2, 'CDS2 Value2']);

  Listbox1.Items.AddObject(Datasource1.Name, DataSource1);
  Listbox1.Items.AddObject(Datasource2.Name, DataSource2);

//  SetDataSource(DataSource1);
end;

procedure TCDSForm.ListBox1Click(Sender: TObject);
var
  Index : Integer;
begin
  Index := Listbox1.ItemIndex;
  SetDataSource(TDataSource(Listbox1.Items.Objects[Index]));
end;

procedure TCDSForm.SetDataSource(const Value: TDataSource);
var
  Index : Integer;
begin
  FDataSource := Value;
  DatasetDelete1.DataSource := FDataSource;
  Index := ListBox1.Items.IndexOf(Value.Name);
  if Index <> ListBox1.ItemIndex then
    ListBox1.ItemIndex := Index;
  Caption := 'Active DS ' + FDataSource.Name;
end;
Другие вопросы по тегам