Действие набора данных 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 := ...
вы обнаружите, что он вызывается несколько раз во время работы приложения.
Теперь попробуйте это с моим кодом ниже:
Отключите две DBGrids от их соответствующего источника данных, затем скомпилируйте и запустите:
Результат: пункт меню DataSetDelete отключен (!).
Затем подключите DBGrid2 к DataSource2. Скомпилируйте и запустите.
Результат: пункт меню DataSetDelete включен, и при щелчке по нему текущая строка удаляется из CDS2.
Затем подключите 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;