How to find out which DB-Aware controls are linked to a TDataSource?
Asked Answered
O

1

7

I have DataSource1 (TDataSource), and there are some DB-Aware controls linked to it (via SomeDBControl.DataSource=DataSource1)

How can I find out (enumerate) in code which controls are linked to a given TDataSource?

Opponent answered 23/6, 2014 at 8:26 Comment(6)
@TLama, DataLinks is a list of TDataLink. and VisualControl is boolean. but which control is it?Opponent
@TLama, very nice, with TFieldDataLink I can access "Control" property, BUT with TGridDataLink.FGrid is private member :( If no one will give a better answer (that includes the TDBGrids) I'll accept your comment.Opponent
@TLama, I have missed that. please post as answer and I'll gladly accept!Opponent
Sorry, taking back all my comments from here... With DataLinks, there is no reliable way to get access to instances for all types of linked controls. The only remaining way I can think of is iterate all the controls and ask their DataSource property. @MartynA, unfortunately wasn't...Snobbery
@TLama: Oh well. Couldn't the OP do it using old style RTTI, by looking for components' DataSource and MasterSource properties?Incense
Depending on what controls you are using in your app, you may still be able to get away with using the Data Links. At the very least, using them might limit the scope of the searching in other controls. If you know what controls you are using that internally use TFieldDataLink and which ones don't then you could build a generic solution to handle the first case and another for the rest.Venturous
I
2

The code below, which uses RTTI, works for me in D7 to list the components which have a DataSource or MasterSource property by recursively searching a container object (i.e. the form and its components).

(Obviously, you could do similar for any other forms/datamodules you're interested in)

Update #1: The original version of this answer produced a list of every component on a form and the name of its DataSource/MasterSource, if any. I've changed it to provide a better match with what you ask in the body of your q).

Update #2: This fixes a couple of slips in the Update #1 version, and re-implements the HasDataSource function in a way which avoids generating an exception when examining components which don't have a DataSource/MasterSource property.

function HasDataSource(AComponent : TComponent; var ADataSource : TDataSource) : Boolean;

  function GetDataSource(APropName : String) : TDataSource;
  var
    AObject : TObject;
    PInfo : PPropInfo;
  begin
    Result :=  Nil;
    PInfo := GetPropInfo(AComponent, APropName);
    if PInfo = Nil then
      exit;
    AObject := GetObjectProp(AComponent, PInfo);
    Result := TDataSource(AObject);
  end;

begin
  Result :=  False;
  ADataSource := GetDataSource('DataSource');
  if ADataSource <> Nil then
    Result := True;
  if Result then exit;

  ADataSource := GetDataSource('MasterSource');
  if ADataSource <> Nil then
    Result := True;
end;


procedure TForm1.Log(Msg: String);
begin
  Memo1.Lines.Add(Msg);
end;

procedure TForm1.FindDataSourceObjects(AContainer : TComponent);
var
  i : Integer;
  ADataSource : TDataSource;

  procedure LogDataSourceName(AContainer : TComponent);
  begin
    Log(AContainer.Name + ' Datasource: ' + ADataSource.Name);
  end;

begin
  if HasDataSource(AContainer, ADataSource) then
    LogDataSourceName(AContainer);

  for i := 0 to AContainer.ComponentCount - 1 do begin
    FindDataSourceObjects(AContainer.Components[i]);
  end;
end;

procedure TForm1.btnFindClick(Sender: TObject);
begin
  FindDataSourceObjects(Self);
end;

The DFM of my form is

object Form1: TForm1
  Left = 195
  Top = 124
  Width = 623
  Height = 303
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object DBText1: TDBText
    Left = 307
    Top = 56
    Width = 65
    Height = 17
    DataSource = DataSource1
  end
  object Panel1: TPanel
    Left = 307
    Top = 80
    Width = 281
    Height = 161
    Caption = 'Panel1'
    TabOrder = 0
    object DBText2: TDBText
      Left = 24
      Top = 64
      Width = 65
      Height = 17
      DataSource = DataSource2
    end
  end
  object Memo1: TMemo
    Left = 8
    Top = 16
    Width = 281
    Height = 225
    TabOrder = 1
  end
  object btnFind: TButton
    Left = 307
    Top = 16
    Width = 75
    Height = 25
    Caption = 'Find'
    TabOrder = 2
    OnClick = btnFindClick
  end
  object DataSource1: TDataSource
    DataSet = ClientDataSet1
    Left = 448
    Top = 16
  end
  object DataSource2: TDataSource
    DataSet = ClientDataSet2
    Left = 544
    Top = 16
  end
  object ClientDataSet1: TClientDataSet
    Aggregates = <>
    Params = <>
    Left = 408
    Top = 16
  end
  object ClientDataSet2: TClientDataSet
    Aggregates = <>
    MasterSource = DataSource1
    PacketRecords = 0
    Params = <>
    Left = 496
    Top = 16
  end
end
Incense answered 23/6, 2014 at 15:51 Comment(3)
You could safely write Result := (IsPublishedProp(AComponent, 'DataSource') and PropIsType(AComponent, 'DataSource', tkClass) and (GetObjectProp(AComponent, 'DataSource', TDataSource) = ADataSource)) or.... How I loved that old style RTTI :)Snobbery
@TLama: Thanks. It had been bothering me that my previous version would generate exceptions for components that don't have DataSource/MasterSource, and there could be hundreds of those in a project. I've re-coded my HasDataSource function in a way which avoids this while only involving one access of the property info per property name.Incense
A paranoic approach would also check if the property is holding an object (if PInfo^.PropType^^.Kind = tkClass) and if so, whether the object that you obtain is really of type TDataSource (addressing the typecast). If someone would make an (evil :-) component that would have published DataSource property of type e.g. Integer, or the object class would differ from TDataSource, the function would fail. [+1ed already since RTTI is the right way to go]Snobbery

© 2022 - 2024 — McMap. All rights reserved.