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 returnerror
(true/false) and in the latter case anErrorText
- optionally, define
OnValidateDrawValue
to change icon : setErrorType
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;
Thank You…