Update: I think I now know understand what you're actually asking, which is that even if you leave the DataSetDelete action's DataSource unassigned, how is it that the
action manages somehow to "know" which datasource to operate upon?
There is an explanation for this if any of your datasources are connected to TDBGrids
or any other DB-aware component whose datalink contains code similar to the following:
function TCustomDBGrid.UpdateAction(Action: TBasicAction): Boolean;
begin
Result := (DataLink <> nil) and DataLink.UpdateAction(Action);
end;
If you put a breakpoint on the Result := ...
you will find that it is called
repeatedly while the app is running.
Now try this with my code below:
Disconnect the two DBGrids from their repective datasource, then compile and run:
Result: The DataSetDelete menu item is disabled(!).
Next connect DBGrid2 to DataSource2. Compile and run.
Result: The DataSetDelete menu item is enabled and clicking it deletes the
current row from CDS2.
Next connect DBGrid1 to DataSource1. Compile and run.
Result: The DataSetDelete menu item is enabled and clicking it deletes the current row from CDS1.
As you can see, unless your code explicitly sets the DataSetAction's DataSource
property, this action operates on the datasource of the first datalink which
returns True from a DB-aware component's UpdateAction
function.
In other words, it's not so much that the DataSetDelete action "knows" which
datasource to use if you leave the DataSetDelete action's DataSource property unassigned, but rather that the DB-aware components' datalinks which
tell the action which datasource is active.
Update 2 To see what I mean, remove any handler you have have at the moment for the DataSetDelete's OnExecute. Then, put a breakpoint on TDataSetDelete.ExecuteTarget
in DBActns.Pas. When it trips, look at the call stack, and you'll find that it is being called from TCustomDBGrid.ExecuteAction, so the identity of the dataset is being passed to the DataSetDelete action, so I think that there is no way to find out the dataset's identity from the DataSetDelete action itself.
However, there is a simple way around this. Attach the following BeforeDelete handler to each of your datasets:
procedure TCDSForm.CDS1BeforeDelete(DataSet: TDataSet);
begin
if MessageDlg(DataSet.Name + ': Delete record?', mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
Abort;
end;
Then, whether you attempt to delete a dataset record, whether using the DataSetDelete action or not, this event handler will be called, and if the user doesn't respond "Yes", the Abort procedure is called, raising a silent exception which prevents the deletion from proceeding.
==============
Original answer follows. I'll tidy it up later
If I'm understanding you correctly, the sample project below should do what you want.
My question is: how can I read the active datasource-componentname
The answer to this is that the TDataSetDelete action has a DataSource property
and it's just a question of setting that to which datasource you want to be active.
"Delphi knows what datasource is active" No, of course it doesn't, how could it possibly until you tell it what datasource you want the DataSetDelete action to operate upon? And the way you do that is to set its DataSource
property. To follow what I mean, compile the code below, set a breakpoint on
Caption := 'Execute'
inside DataSetDelete1Execute
, then compile & run the project and click the DataSetDelete1Execute menu item. When the breakpoint trips, evaluate
`TDataSetDelete(Sender).DataSource. You will find the value is Nil, because you haven't (yet) told the action which datasource the action is to operate upon.
Then, uncomment the line
SetDataSource(DataSource1);
in FormCreate
and repeat the evaluation. Now the action knows, because you've told it, which datasource it should consider to be active.
In case you missed it first time, it is the line
DatasetDelete1.DataSource := FDataSource;
in procedure TCDSForm.SetDataSource(const Value: TDataSource)
which sets the dataset that the DatasetDelete1 action uses.
Btw, there is no "magic" to any of this - look at the Delphi source
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;
and
procedure TDataSetDelete.ExecuteTarget(Target: TObject);
begin
GetDataSet(Target).Delete;
end;
Code (Updated):
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;