views:

181

answers:

3

In my project there is a TADOQuery tdm_Company that gets filled with a set of fields, provided with proper labels and fields set to visible=false where appropriate.The query returns a single result.
I have a detail screen that needs a bunch of labels and edit textboxes for these fields. Is it possible to auto generate these in the editor? What if I need those controls to be controls from the DevExpress components (TcxDBTextEdit and TcxLabel for example)?

+1  A: 

I have never tried this, but there is (or was? - sorry, can't check) a Database Form Wizard. If you want to have other controls than those the wizard generates, there are possibilities to change these afterwards, e.g. GExperts' Replace Components.

Ulrich Gerhardt
I can just drag in the fields from the query field box to my form and then I'll have a look at GExperts
borisCallens
+1  A: 

In a very similar case (a query to return a single record showing contact data from an entity - company, customer etc.) we use DevExpress's TcxDBVerticalGrid. It scales much better and is more flexible (especially when resizing the form) when it comes to display a bunch of data which represents a single object.

Of course, you are not tied to the above component, you can obtain good results with (almost) any vertical grid / DBIspector but since you asked about a DevExpress component I gave you the above solution.

HTH

interesting. I'm new to Delphi and am basically still learning what my toolset contains. Will check this component out..
borisCallens
I don't seem to have this control. What version of devexpress are you referring to?
borisCallens
DevExpress is a company which sells more packages and depending on what you purchased you might have (or not) that component. It is quite similar with the Object Inspector from Delphi's IDE.Of course, if you want, you can purchase it - see http://www.devexpress.com/Products/vcl/ExVerticalGrid/ - but be aware that there are other (free) alternatives. Also, you can use your TcxGrid and fake it to show as a vertical one by stacking bands accordingly in a DB Banded Table view. Also (perhaps better) you can use DB Cards view of your TcxGrid. See TcxGrid's demos for this.
Hmm, I don't think I'm going to propose component purchases on my 2nd week ;) But I'll check your suggestions
borisCallens
+1  A: 

Hi,

A long time ago I actually created my own Wizard for this, based on an actual Custom Form I wrote for a FrameWork of mine. When the Dialog for the wizard was shown, it would display all fields in a grid and allow the user to indicate which component should be used to display that field.

In my case depending on the Type of field it was prefilled with specific components (eg a TcxDateEdit for a TDateTime field, ...). The user could still change that though, and indicate which fields he wanted to add to the form. Once the user closes the form it was just matter of itterating over all the fields and creating the corresponding control.

Searched through my code and found this back :

{ Custom Devia Development Framework RecordView Module which adds functionality to
  create the DB Aware Controls for the RecordView }
TDevFrameWorkRecordViewModule = class( TCustomModule )
protected
  procedure CreateDBAwareComponents( aParent : TComponent; aDataSource : TDataSource; aFields : TFields; aFieldDefs : TFieldDefs ); virtual;
  function DefaultWizardClass : TDBAwareControlWizardClass; virtual;
  function DefaultLabelClass  : TComponentClass; virtual;
  function  MaxFieldCaptionLength  ( aFields : TFields ) : Integer; virtual;
protected
  function GetSelectedComponents : IDesignerSelections;
  function GetSelectedControl    : TControl;

  property SelectedControl    : TControl            read GetSelectedControl;
  property SelectedComponents : IDesignerSelections read GetSelectedComponents;
public
  procedure DevAddDBAwareComponentsWizard( aParent : TControl ); virtual;
  procedure ExecuteVerb(Index: Integer); override;
  function GetVerb(Index: Integer): string; override;
  function GetVerbCount: Integer; override;
end;

...

procedure TDevFrameWorkRecordViewModule.CreateDBAwareComponents(
  aParent : TComponent; aDataSource : TDataSource; aFields : TFields; aFieldDefs : TFieldDefs );
var
  lcv : Integer;
  aLabel : TControl;
  aEdit  : TWinControl;

  aDataBinding : TcxDBEditDataBinding;

  aTop   , aLeft  : Integer;
  aWidth          : Integer;
  aMaxCaptionWidth: Integer;
  aDBLeft         : Integer;
  aRecordView     : IDevFrameWorkRecordView;
  aDBAwareClass   : TComponentClass;
  aDBAwareVisible : Boolean;
  aWizardForm     : TfrmDevFrameWorkAddDataAwareControlsWizard;
begin
  { First make sure the procedure was triggered on a FrameWorkRecordView }
  if ( Supports( Root, IDevFrameWorkRecordView, aRecordView ) ) then
  begin
    { Now Create and Show the wizard so the user can specify all the options }
    aWizardForm := DefaultWizardClass.Create( Nil );
    try
      aWizardForm.RecordDataSet := aRecordView.DataSource.DataSet;
      aWizardForm.InitialiseSettings;

      { If the user closed the Wizard  using the OK button, we can continue the
        process }
      if ( aWizardForm.ShowModal = mrOK ) then
      begin
        { By default the label components should start at 8,8 in the Parent Container }
        aTop  := 8;
        aLeft := 8;
        aWidth:= 121;
        aMaxCaptionWidth := MaxFieldCaptionLength( aFields );

        { Now set the intial Left Position for our DBAware controls according
          to the MaxCaptionWidth }
        aDBLeft := 24 + ( ( ( aMaxCaptionWidth div 8 ) + 1 ) * 8 );

        { Loop over all fields to create the Label and DBAwareComponent }
        for lcv := 0 to Pred( aFields.Count ) do
        begin
          { Get some settings from the Wizard form }
          aDBAwareClass := aWizardForm.GetDBAwareComponentClass( aFields[ lcv ] );
          aDBAwareVisible := aWizardForm.GetDBAwareComponentVisible( aFields[ lcv ] );

          { Only create the components if the user indicated he wants to see them }
          if aDBAwareVisible then
          begin
            { Now create the Label and the DBAware Control }
            aLabel := TControl   ( Designer.CreateComponent( DefaultLabelClass, aParent, aLeft , aTop, aMaxCaptionWidth, 17 ) );
            aEdit  := TWinControl( Designer.CreateComponent( aDBAwareClass, aParent, aDBLeft, aTop, aWidth, 21 ) );

            { Now Set the Label Properties }
            aLabel.Name        := Designer.UniqueName( 'cxlbl' + aFields[ lcv ].FieldName );
            aLabel.HelpType    := htKeyWord;
            aLabel.HelpKeyword := Root.Name + '.' + aFields[ lcv ].FieldName;

            { Set the additional properties using RTTI }
            if ( IsPublishedProp( aLabel, 'FocusControl' ) ) then
            begin
              SetObjectProp( aLabel, 'FocusControl', aEdit );
            end;
            if ( IsPublishedProp( aLabel, 'Caption' ) ) then
            begin
              SetStrProp( aLabel, 'Caption', aFields[ lcv ].DisplayLabel );
            end;

            { Now set the Edit Properites }

            aEdit.Name        := Designer.UniqueName( {'cxlbl' +} aFields[ lcv ].FieldName );
            aEdit.HelpType    := htKeyWord;
            aEdit.HelpKeyword := Root.Name + '.' + aFields[ lcv ].FieldName;

            { Set the additional properties using RTTI }
            if ( IsPublishedProp( aEdit, 'DataBinding' ) ) then
            begin
              aDataBinding := TcxDBEditDataBinding( GetObjectProp( aEdit, 'DataBinding' ) );
              SetObjectProp( aDataBinding, 'DataSource', aDataSource );
              SetStrProp   ( aDataBinding, 'DataField' , aFields[ lcv ].FieldName );
            end;

            if ( aEdit is TcxCustomDropDownEdit ) then
            begin
              aEdit.Width := aWidth + 16;
            end;

            { Now increment the Top position for the next control }
            inc( aTop, ( ( ( aEdit.Height div 8 ) + 1 ) * 8 ) );
          end;
        end;
      end;
    finally
      FreeAndNil( aWizardForm );
    end;
  end;
end;

function TDevFrameWorkRecordViewModule.DefaultLabelClass: TComponentClass;
begin
  Result := TLabel;
end;

function TDevFrameWorkRecordViewModule.DefaultWizardClass: TDBAwareControlWizardClass;
begin
  Result := TfrmDevFrameWorkAddDataAwareControlsWizard;
end;

procedure TDevFrameWorkRecordViewModule.ExecuteVerb(Index: Integer);
var
  aSelections : IDesignerSelections;
  lcv         : Integer;
begin
  aSelections := TDesignerSelections.Create;
  Designer.GetSelections( aSelections );
  for lcv := 0 to Pred( aSelections.Count ) do
  begin
    {$IFDEF CODESITE}
    csFWRecordView.Send( 'aSelection.Items[ lcv ]', aSelections.Items[ lcv ] );
    {$ENDIF}
  end;

  Case Index of
    0 : DevAddDBAwareComponentsWizard( SelectedControl );
    else Inherited ExecuteVerb( Index );
  end;
end;

{*****************************************************************************
  This function will be used to return a list of selected components on the
  current designer.

  @Name       TDevFrameWorkRecordViewModule.GetSelectedComponents
  @author     Devia - Stefaan Lesage
  @param      None
  @return     None
  @Exception  None
  @See        None
******************************************************************************}

function TDevFrameWorkRecordViewModule.GetSelectedComponents: IDesignerSelections;
begin
  Result := TDesignerSelections.Create;
  Designer.GetSelections( Result );
end;

function TDevFrameWorkRecordViewModule.GetSelectedControl: TControl;
var
  lcv : Integer;
begin
  Result := Nil;
  if ( Assigned( SelectedComponents ) ) then
  begin
    if ( SelectedComponents.Count <> 0 ) then
    begin
      for lcv := 0 to Pred( SelectedComponents.Count ) do
      begin
        if ( SelectedComponents.Items[ lcv ] is TControl ) then
        begin
          Result := TControl( SelectedComponents.Items[ lcv ] );
          Break;
        end;
      end;
    end;
  end;
end;

function TDevFrameWorkRecordViewModule.GetVerb(Index: Integer): string;
begin
  Case Index of
    0 : Result := 'Dev.AddDataAwareComponents';
  end;
end;

function TDevFrameWorkRecordViewModule.GetVerbCount: Integer;
begin
  Result := 1;
end;

{*****************************************************************************
  This function will determine the length of the Longest field's caption.

  @Name       TDevFrameWorkRecordViewModule.MaxFieldCaptionLength
  @author     Devia - Stefaan Lesage
  @param      None
  @return     Returns the length of the longest field's catpion.
  @Exception  None
  @See        None
******************************************************************************}

function TDevFrameWorkRecordViewModule.MaxFieldCaptionLength(
  aFields: TFields): Integer;
var
  aMaxCaptionWidth : Integer;
  aCanvas          : TCanvas;
  lcv              : Integer;
  aCaption         : String;
begin
  aMaxCaptionWidth := 0;

  { First Determine how long the largest caption will be }
  aCanvas := TDevFrameWorkRecordView( Root ).Canvas;

  { Loop over each field to dertermin which caption is the longest one }
  for lcv := 0 to Pred( aFields.Count ) do
  begin
    if ( aFields[ lcv ].DisplayLabel <> '' ) then
    begin
      aCaption := aFields[ lcv ].DisplayLabel;
    end
    else
    begin
      aCaption := aFields[ lcv ].FieldName;
    end;
    if ( aCanvas.TextWidth( aCaption ) >
         aMaxCaptionWidth ) then
    begin
      aMaxCaptionWidth := aCanvas.TextWidth( aCaption );
    end;
  end;

  { Return the Length of the Longest Caption }
  Result := aMaxCaptionWidth;
end;

procedure TDevFrameWorkRecordViewModule.DevAddDBAwareComponentsWizard( aParent : TControl );
var
  aRecordView : IDevFrameWorkRecordView;
  aDataSource : TDataSource;
begin
  {$IFDEF CODESITE}
  csFWRecordView.EnterMethod( Self, 'DevAddDBAwareComponentsWizard' );
  {$ENDIF}

  if ( Supports( Root, IDevFrameWorkRecordView, aRecordView ) ) then
  begin
    {$IFDEF CODESITE}
    csFWRecordView.SendMsg( csmInfo, 'Root supports I®FrameWorkRecordView' );
    {$ENDIF}

    aDataSource := TDataSource( Designer.GetComponent( 'srcMain' ) );

    if ( Assigned( aDataSource ) ) and
       ( Assigned( aDataSource.DataSet ) ) then
    begin
      {$IFDEF CODESITE}
      csFWRecordView.SendMsg( csmInfo, 'aRecordView.DataSource Assigned' );
      csFWRecordView.SendMsg( csmInfo, 'aRecordView.DataSource.DataSet Assigned' );
      {$ENDIF}

      CreateDBAwareComponents( aParent, aDataSource, aDataSource.DataSet.Fields, aDataSource.DataSet.FieldDefs );
    end;
  end;

  {$IFDEF CODESITE}
  csFWRecordView.ExitMethod( Self, 'DevAddDBAwareComponentsWizard' );
  {$ENDIF}
end;

Of course this won't compile for you. It is something I wrote for a development framework in Delphi 7 a few years ago. It should give you an idea though on how you could actually do it.

Regards,

Stefaan

Stefaan
Cool. I'm sure this can come in handy in the future. For now I found that just creating default controls by dragging them from the Tqry or Tsp and then replacing them with GExperts is an acceptably fast way to go.
borisCallens