DevExpress tips

My own tips using DevExpress products

cxGrid

Open different forms depending on what cell was doubleclicked :

procedure TFSaisieOEU.VGroupesDblClick(Sender: TObject);
var APoint: TPoint;
    AHitTest: TcxCustomGridHitTest;
    ARecord: TcxCustomGridRecord;
begin
  GetCursorPos(APoint);
  with VGroupes.Site do begin
    APoint := ScreenToClient(APoint);
    AHitTest := ViewInfo.GetHitTest(APoint);
    if AHitTest is TcxGridRecordCellHitTest then begin
      ARecord := TcxGridRecordHitTest(AHitTest).GridRecord;
      if TcxGridRecordCellHitTest(AHitTest).Item=VGroupesnomcontrat then begin
        if not VarIsNull(ARecord.Values[VGroupesidcontrat.Index]) then
          TFFicheContrat.Create(Application,'idcontrat='+IntToStr(ARecord.Values[VGroupesidcontrat.index]));
      end
      else
        TFAtelierCatalogue.Create(Application,'id='+IntToStr(ARecord.Values[VGroupesidgr.Index]));
    end;
  end;
end;

FocusedRecord, SelectedRecords, FilteredRecords…

Visible records / Filtered records

VMethodes.BeginUpdate;
For i:=0 to VMethodes.DataController.FilteredRecordCount-1 do begin
  RecNo:=VMethodes.DataController.FilteredRecordIndex[i];
  VMethodes.DataController.Values[RecNo,VMethodesCheck.Index]:=True;
end;
VMethodes.EndUpdate;

Selected records :

VMethodes.BeginUpdate;
For i:=0 to VMethodes.DataController.Controller.SelectedRecordCount-1 do Begin
  RecNo:=VMethodes.DataController.FilteredRecordIndex[i];
  VMethodes.DataController.Values[RecNo,VMethodesCheck.Index]:=True;
End;
VMethodes.EndUpdate;

Focused record :

VFamilles.Controller.FocusedRecord.Values[VFamillesnom.Index];

Setting up Master-Detail

The documentation is very precise but NEVER FORGET to ORDER BY the query of the lookup (detail) by the link field.

Configure, save and restore columns

To open the customization window :

ViewDG.Controller.Customization := true;

You can place a TCxGridPopupMenu on the form and connect the Grid property.

To save/restore :

procedure TMyForm.FormCreate(Sender: TObject);
begin
  ViewDG.RestoreFromRegistry(RegistryPrefix+'\VisuRepartDG\Main');
end;

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  ViewDG.StoreToRegistry(RegistryPrefix+'\VisuRepartDG\Main');
end;

Navigator

Empty (to show only InfoPanel) :

    Navigator.Buttons.First.Visible = False
    Navigator.Buttons.PriorPage.Visible = False
    Navigator.Buttons.Prior.Visible = False
    Navigator.Buttons.Next.Visible = False
    Navigator.Buttons.NextPage.Visible = False
    Navigator.Buttons.Last.Visible = False
    Navigator.Buttons.Insert.Visible = False
    Navigator.Buttons.Append.Visible = False
    Navigator.Buttons.Delete.Visible = False
    Navigator.Buttons.Edit.Visible = False
    Navigator.Buttons.Post.Visible = False
    Navigator.Buttons.Cancel.Visible = False
    Navigator.Buttons.Refresh.Visible = False
    Navigator.Buttons.SaveBookmark.Visible = False
    Navigator.Buttons.GotoBookmark.Visible = False
    Navigator.Buttons.Filter.Visible = False
    Navigator.InfoPanel.DisplayMask = '[RecordIndex] / [RecordCount]'
    Navigator.InfoPanel.Visible = True
    Navigator.Visible = True

Only Insert/Delete/Post/Cancel :

    Navigator.Buttons.First.Visible = False
    Navigator.Buttons.PriorPage.Visible = False
    Navigator.Buttons.Prior.Visible = False
    Navigator.Buttons.Next.Visible = False
    Navigator.Buttons.NextPage.Visible = False
    Navigator.Buttons.Last.Visible = False
    Navigator.Buttons.Insert.Visible = True
    Navigator.Buttons.Append.Visible = False
    Navigator.Buttons.Delete.Visible = True
    Navigator.Buttons.Edit.Visible = False
    Navigator.Buttons.Post.Visible = True
    Navigator.Buttons.Cancel.Visible = True
    Navigator.Buttons.Refresh.Visible = False
    Navigator.Buttons.SaveBookmark.Visible = False
    Navigator.Buttons.GotoBookmark.Visible = False
    Navigator.Buttons.Filter.Visible = False

Add custom buttons to any TcxEditor :

with  TcxCustomEditProperties(EPeriode.Properties) do begin
  Images:=DM.ilFichiers;
  with Buttons.Add as TcxEditButton do begin
    Kind := bkGlyph;
    ImageIndex:=15;
    Default:=False;
    LeftAlignment := True;
  End;
  OnButtonClick:=EPeriodePropertiesButtonClick;
end;

procedure TFSaisieDroitsDirects.EPeriodePropertiesButtonClick(Sender: TObject; AButtonIndex: Integer);
begin
  Case AButtonIndex of
    0: DoSomething;
  End;
end;

Drag & Drop

OnDragOver

Var
  vw:TcxGridDBTableView;
begin
  If TDragControlObject(Source).Control is TcxGridSite then begin
    vw:=TcxGridDBTableView(TCxGridSite(TDragControlObject(Source).Control).GridView);
    if vw.DataController.GetItemByFieldName('idcontrat')<>Nil then begin
      Accept:=True;
  //  end
  //  else begin
  //    ShowMessage(Err_YouMustDragAContractHere);
    end;
  end;
end;

OnDragDrop

var
  vw:TcxGridDBTableView;
begin
  If TDragControlObject(Source).Control is TcxGridSite then begin
    vw:=TcxGridDBTableView(TCxGridSite(TDragControlObject(Source).Control).GridView);
    if vw.DataController.GetItemByFieldName('idcontrat')<>Nil then begin
      fidx:=vw.DataController.GetItemByFieldName('idcontrat').Index;
      // do whatever here on vw.Controller.SelectedRecords[i].Values[fidx]
    end;
  end;
end;

Validation

Validation in Grids

  • define each field’s cxGridTableViewColumnFirstName.Properties.ValidationOptions
  • each field’s properties.OnValidate should return error (true/false) and in the latter case an ErrorText
  • optionally, define OnValidateDrawValue to change icon : set ErrorType to [eetNone, eetError, eetWarning, eetInfo, eetCustom] ; for eetCustom, also set ErrorIcon to some bitmap (like imagelist1image1.picture.bitmap).
  • it seems you have to setup a ValidateDrawValue procedure to see the icons.

DevExpress define one FirstNameError that returns true on error/warning and set the message then use this function in Properties.OnValidate and OnValidateDrawValue.

function TMain.FirstNameError(const AValue: Variant; var AErrorText: TCaption): Boolean;
begin
  Result := VarToStr(AValue) = '';
  if Result then
    AErrorText := 'Please enter a value';
end;

procedure TMain.viewFirstNamePropertiesValidate(Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption; var Error: Boolean);
begin
  Error := FirstNameError(DisplayValue, ErrorText); 
end;

procedure TMain.viewFirstNameValidateDrawValue(Sender: TcxCustomGridTableItem; ARecord: TcxCustomGridRecord; const AValue: Variant; AData: TcxEditValidateInfo);
var AErrorText: TCaption; 
begin 
  if FirstNameError(AValue, AErrorText) then begin
    AData.ErrorType := eetError; 
    AData.ErrorText := AErrorText; 
  end;
end;

To enforce validation checking inside the dataset/query’s OnBeforePost :

procedure TfrmAffaireCommunications.qCommunicationsBeforePost(DataSet: TDataSet);
var
  i: Integer;
  col:TcxCustomGridTableItem;
  etext:TCaption;
  err:Boolean;
  v:Variant;
begin
  for i := 0 to vCommunications.ItemCount-1 do begin
    col:=TcxCustomGridTableItem(vCommunications.Items[i]);
    if (col.Properties<>Nil) and Assigned(col.Properties.OnValidate) then begin
      v:=col.EditValue;
      col.Properties.OnValidate(col,v,etext,err);
      if Err then begin
        ShowMessage(etext);
        col.Focused:=True;
        Abort;
      end;
    end;
  end;
end;

Validation in standalone editors

Define each field’s Properties.ValidationOptions

Create an OnValidate for each field :

procedure TfrmAffaireGeneralites.EDateRequisitionPropertiesValidate(Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption; var Error: Boolean);
begin
  if DM.qGlobalAffdaterequisition.IsNull then begin
    ErrorText:='Enter a value';
    Error:=True;
  end;
end;

Define Form.Validate method like this :

procedure TfrmAffaireGeneralites.Validate; 
begin
  EField1.ValidateEdit;
  EField2.ValidateEdit;  
end;

And call this to update editors (Form’s OnCreate, AfterScroll, BeforePost)

To enforce validation checking inside the dataset/query’s OnBeforePost :

First define these types to access Properties :

type
  TcxCustomEditPropertiesAccess = class(TcxCustomEditProperties);
  TcxCustomEditAccess = class(TcxCustomEdit);

Then :

procedure TfrmAffaireGestionCommerciale.myGlobalAffBeforePost(DataSet: TDataSet);
var
  i: Integer;
  ed:TcxCustomEditAccess;
  edp:TcxCustomEditPropertiesAccess;
  etext:TCaption;
  v:Variant;
begin
  for i := 0 to ComponentCount-1 do
    if Components[i] is TCxCustomEdit then begin
      ed:=tcxCustomEditAccess(Components[i]);
      edp:=TcxCustomEditPropertiesAccess(ed.properties);
      if (edp<>Nil) and Assigned(edp.OnValidate) then begin
        v:=ed.EditValue;
        if ValidationFeedback(ed,v,etext)=eetError then begin
          ShowMessage(etext);
          ed.SetFocus;
          Abort;
        end;
      end;
    end;
end;

My framework looks like this : a unique function to check all fields (a result of eetError means you won’t be able to Post while eetWarning will just display an icon) and two OnValidate and OnValidateDrawValue used by all fields, columns, etc. that need validation. The tricky part is to guess what Sender in each case :

  • for normal editors : if Sender=cxEdit1
  • for TcxVerticalGrid : if Sender=column.properties
  • for TcxGrid : if Sender=column

This way, validation is centralized with a form/frame.

function TFMain.ValidationFeedback(const Sender:TObject; const AValue: Variant; Var AErrorText: TCaption) : TcxEditErrorType;
begin
  Result:=eetNone;
  AErrorText:='';
  if Sender=vgComposantsout_datearrivee.Properties then begin
    if (qComposantstypecomposant.Value=0) and (qComposantsout_datearrivee.IsNull) then begin
      AErrorText:='Vous devez indiquer une date d''arrivée';
      Result:=eetWarning;
    end;
  end;
end;

These two procedures are the same for all validation fields :

procedure TFMain.vgComposantsnumeroPropertiesValidateDrawValue(
  Sender: TcxCustomEditorRowProperties; ARecordIndex: Integer;
  const AValue: Variant; AData: TcxEditValidateInfo);
var s:TCaption;
begin
  AData.ErrorType:=ValidationFeedback(Sender,AValue,s);
  adata.ErrorText:=s;
end;

procedure TFMain.vgComposantsout_datearriveeEditPropertiesValidate(
  Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption;
  var Error: Boolean);
var etype:TcxEditErrorType;
begin
  etype:=ValidationFeedback(Sender,DisplayValue,ErrorText);
  Error:=eType<>eetNone;
end;

Misc

Deactivating MouseWheel from comboboxes

Define an OnMouseWheel method somewhere (I place it in the Datamodule, public section so it can be used from anywhere) :

procedure TDM.NoMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  Abort;
end;

Then declare this at the top of the unit using the combobox :

Type TCxDBLookupComboBoxAccess = Class(TcxDBLookupComboBox);

Finally set an event for the combo :

TCxDBLookupComboBoxAccess(cbSociete).OnMouseWheel:=DM.NoMouseWheel;

Or, if you have many, in the OnCreate or OnShow :

for i := 0 to ComponentCount-1 do
  if Components[i] is TcxDBLookupComboBox then
    TCxDBLookupComboBoxAccess(Components[i]).OnMouseWheel:=DM.NoMouseWheel;

Une réflexion sur « DevExpress tips »

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *