0

Delphi 的奇妙之处之一是 TActionlist。更好的是默认的 TActions,比如 TDataset-actions。我有一个带有几个简单表格的表格。所以我让 Delphi 通过几个 TDatasetinsert/delete/edit 等来决定哪个数据源/表处于活动状态。

但现在我希望删除操作有一个对话框“你确定”之类的。如果我干预动作的执行事件,则动作似乎在对话后停止。所以我想像 somedatasource.dataset.delete 一样自己做删除操作。但我不知道这个 TDatasetdelete 是哪个数据源处于活动状态。

TDatasetdelete 有一个 datasource 属性,但默认为 nil 并且读取它会导致访问冲突。即使我不分配它,当 TDatasetdelete 执行时,也会从我的一个数据源中删除一个数据行。在这种情况下,我如何找出哪个数据源是“活动的”,换句话说,它在执行时将使用哪个数据源。

4

1 回答 1

4

更新:我想我现在知道你真正要问的是什么,即即使你不分配 DataSetDelete 操作的 DataSource,该操作如何以某种方式管理以“知道”要对哪个数据源进行操作?

如果您的任何数据源连接到 TDBGrids 或任何其他数据链路包含类似于以下代码的 DB 感知组件,则对此有解释:

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

如果您在 上放置断点,Result := ...您会发现它在应用程序运行时被重复调用。

现在用下面的代码试试这个:

  1. 断开两个 DBGrid 与其相应数据源的连接,然后编译并运行:

    结果:DataSetDelete 菜单项被禁用(!)。

  2. 接下来将 DBGrid2 连接到 DataSource2。编译并运行。

    结果:DataSetDelete 菜单项已启用,单击它会从 CDS2 中删除当前行。

  3. 接下来将 DBGrid1 连接到 DataSource1。编译并运行。

    结果: DataSetDelete 菜单项已启用,单击它会从CDS1中删除当前行。

如您所见,除非您的代码显式设置 DataSetAction 的 DataSource 属性,否则此操作对第一个数据链路的数据源进行操作,该数据链路从 DB 感知组件的UpdateAction函数返回 True。

换句话说,如果您不指定 DataSetDelete 操作的 DataSource 属性,DataSetDelete 操作“知道”要使用哪个数据源,而是 DB-aware 组件的数据链接 告诉操作哪个数据源处于活动状态。

更新 2 要了解我的意思,请删除您目前为 DataSetDelete 的 OnExecute 拥有的任何处理程序。然后,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'

,然后编译并运行项目并单击DataSetDelete1ExecuteDataSetDelete1Execute菜单项。当断点跳闸时,评估`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;
于 2016-07-30T17:30:55.723 回答