How to change color of tcxgrid row - delphi

I want to show some rows from tcxgrid in different color (Depend upon column value ).
I did changes for it but its not getting reflected on grid after running my project.
procedure TfrmMessaging.cxGrid1DBTableView1CustomDrawCell(..);
Var
i : Integer;
begin
For i := 0 To cxGrid1DBTableView1.ViewData.RowCount - 1 Do
Begin
If cxGrid1DBTableView1.ViewData.Rows[i].Values[4] = '1' Then
Begin
cxGrid1.Canvas.Brush.Color := clRed;
End;
End;
end;
In above code I have used cxGrid1DBTableView1CustomDrawCell event of tcxgrid.
Thanks in advance.

If you are using a data-aware view (as it seems) you need to use the DataController instead of the ViewData to get to the records.
As stated in DevExpress help for TcxGridDBTableView (bold format is mine):
The TcxGridDBTableView object represents the data-aware version of the grid Table View. It inherits all functionality from its ancestor, except for data binding settings. The DataController.DataSource property of the TcxGridDBTableView provides the connection between the View and a TDataSet or its descendant.
Besides that, the OnCustomDrawCell event fires for every cell, so you do not need to iterate the rows.
Following code should help you:
procedure TfrmMessaging.cxGrid1DBTableView1CustomDrawCell(
Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean);
begin
if Sender.DataController.GetValue(AViewInfo.GridRecord.RecordIndex, 4) = '1' then
ACanvas.Brush.Color := clRed;
end;

Normally the easiest path for stuff like that are cxStyles. Drop a style repository on the form, add one or more styles to it and assign them in the object inspector or in an event handler (OnGetContentStyle etc.).
One advantage over custom drawing is that styles are considered for various calculations while owner drawn cells aren't handled specially and sometimes aren't autosized correctly etc.

How I change the color of the grid
procedure TfrmNewOffer.GrdOffDetailViewRemarkCustomDrawCell(
Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean);
var
backgroundColorCode: Variant;
textColorCode: Variant;
begin
inherited;
if assigned(AViewInfo) and assigned(AViewInfo.GridRecord) then
begin
backgroundColorCode := AViewInfo.GridRecord.Values[GrdOffDetailViewBackColorCode.Index];
textColorCode := AViewInfo.GridRecord.Values[GrdOffDetailViewTextColorCode.Index];
if not VarIsNull(backgroundColorCode) then
begin
ACanvas.Brush.Color := backgroundColorCode;
end;
if not VarIsNull(textColorCode) then
begin
ACanvas.Font.Color := textColorCode;
end;
end;
end;

Related

cxgrid highlight (or color) changed cell on form closequery

On closequery of the form I have :
if MessageDlg('Close program ?',
mtConfirmation, [mbYes,mbCancel],0) <> mrYes then CanClose := False
else if DataModule2.mytable.State in [dsEdit,dsInsert] then
if MessageDlg('Save changes ?', mtConfirmation,
[mbYes,mbNo],0) = mrYes then DataModule2.mytable.Post;
Is there a way I can highlight (or color) a changed cell in cxgrid when I trigger my onclosequery event ?
I don't need to know what was changed but just to know which cell was changed so the user can see it so he can easily decide weather to save the changes or not.
It is simple to get the cxGrid to draw a cell (or row) highlighted in some way using the
cxGrid1DBTableView1CustomDrawCell event. And by having a flag that indicates that the OnCloseQuery event is in progress, you can restrict its action to inside that event.
Update The code I originally posted with this answer could not successfully mark more than one cell in the current grid row as changed. The updated code below can do this however; note the comments in the two
procedures.
type
TForm1 = class(TForm)
[...]
public
QueryingClose : Boolean;
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
try
QueryingClose := True;
//{cxGrid1.Invalidate{True); Do NOT call Invalidate, because it causes the
// grid's repainting logic to operate in a way which effectively makes it
// impossible to mark more that one cell in the current data row as changed
ShowMessage('Close?');
finally
QueryingClose := False;
end;
end;
procedure TForm1.cxGrid1DBTableView1CustomDrawCell(Sender:
TcxCustomGridTableView; ACanvas: TcxCanvas; AViewInfo:
TcxGridTableDataCellViewInfo; var ADone: Boolean);
var
Field : TField;
MarkCell : Boolean;
S1,
S2 : String;
EC : TcxGridTableEditingController;
begin
if QueryingClose and
(TcxGridDBTableView(Sender).DataController.DataSet.State in[dsEdit, dsInsert]) then begin
Field := TcxGridDBColumn(AViewInfo.Item).DataBinding.Field;
S1 := VarToStr(Field.OldValue);
// When this event is called, the user may be in the middle of editing a cell's contents
// So, the purpose of the following lines is to close the inplace editor being used to do
// this amd post the chamged value back to the TField associated with the cell
EC := TcxGridDBTableView(Sender).Controller.EditingController;
if EC.IsEditing then
EC.HideEdit(True);
S2 := VarToStr(Field.Value);
MarkCell := S1 <> S2;
if MarkCell then
ACanvas.Brush.Color := clLime;
end;
end;
For this to work, your TDataSet-descendant type must support correctly returning the original contents of the fields on their OldValue property; TClientDataSet, which I've used to write/test this code certainly does this but I've no idea what actual TDataSet type you're using.
Hopefully, it should be apparent that you could use these two procedures to
build a list of TFields that have changed values, including the FieldName OldValue, and Value.

How to change background color of an TListview item?

How can I customize my listview to display different background colors like in the picture below ?
My listview is bound to a datasource (Livebindng). I want to use the color field to set my backgroud color.
I've customized my view this way :
3 Text items (Designation,Date and Resume)
1 Bitmap item (Couleur)
Text items are bound to datasource but there is no way to bind my Bitmap to my "color" field.
I've filled the listview ActivesUpdateObjects event but this is not enough as bitmap is not changed when datasource record is updated!
procedure TfrmMain.lvTachesActivesUpdateObjects(const Sender: TObject;
const AItem: TListViewItem);
begin
SetItemColor(AItem);
end;
procedure TfrmMain.SetItemColor(const AItem: TListViewItem; const UpdateColor:
Boolean = False);
var
LObject: TListItemImage;
VC: TColor;
begin
LObject := AItem.Objects.FindObjectT<TListItemImage>('Couleur');
VC:= dtmMain.qrTaches.FieldByName('couleur').AsInteger;
if LObject.Bitmap = nil then
begin
LObject.Bitmap := FMX.Graphics.TBitmap.Create(10,240);
LObject.Bitmap.Clear(VC);
end else if UpdateColor then LObject.Bitmap.Clear(VC);
end;
Is there a better way to proceed? I was also looking to use style but it appears (or I didn't find) that itemlistview can apply styles!
Ps : Firemonkey / Windows / Delphi Berlin XE10.1
I'm using Delphi 7 so take this with a grain of salt.
You may have to write your own CustomDrawItem method on your TreeView to handle this stuff
This is mine (I edited out some code because it has some lengthy logic behind). Also, I don't draw icons so the DrawImage part is commented.
procedure TVentanaVisorComponentes.TreeView1CustomDrawItem(
Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
var DefaultDraw: Boolean);
var
NodeRect: TRect;
EsSeleccion, EsDespejado: boolean;
begin
with TreeView1.Canvas do
begin
//If DefaultDraw it is true, any of the node's font properties can be
//changed. Note also that when DefaultDraw = True, Windows draws the
//buttons and ignores our font background colors, using instead the
//TreeView's Color property.
DefaultDraw := False;
//DefaultDraw = False means you have to handle all the item drawing yourself,
//including the buttons, lines, images, and text.
if not DefaultDraw then
begin
Brush.Color := clMenuHighLight;
Font.Color := clWhite;
NodeRect := Node.DisplayRect(True);
FillRect(NodeRect);
// ...
NodeRect := Node.DisplayRect(False);
// ...
FillRect(NodeRect);
NodeRect.Left := NodeRect.Left + (Node.Level * TreeView1.Indent);
//NodeRect.Left now represents the left-most portion of the expand button
DrawButton(NodeRect, Node);
NodeRect.Left := NodeRect.Left + TreeView1.Indent;
//NodeRect.Left is now the leftmost portion of the image.
//DrawImage(NodeRect, Node.ImageIndex);
// NodeRect.Left := NodeRect.Left + ImageList.Width;
//Now we are finally in a position to draw the text.
TextOut(NodeRect.Left, NodeRect.Top, (Node as TNodoArbolComponentes).Texto);
end;
end;
end;

Delphi DBGrid alternate row colors for all DBGrids in the project

How can I make all my grids look the same way all over my forms?
I want to implement an alternate row color that must be applied on all grids of my project. Is it possible without adding the same DrawColumnCell event code for every grid?
I want to avoid adding the same code for each of my grids. I have like 30 grids in my project and multiplied by 13 rows of code it just adds a lot of code lines to my project making it "unfriendly".
I am looking for a solution that will only add 13 lines of code to the project, not 390 lines.
My formatting code looks like this (for example):
procedure TDBGrid.DBGrid1DrawColumnCell(Sender: TObject;const Rect: TRect;DataCol: Integer;Column: TColumn;State: TGridDrawState) ;
var
grid : TDBGrid;
row : integer;
begin
grid := sender as TDBGrid;
row := grid.DataSource.DataSet.RecNo;
if Odd(row) then
grid.Canvas.Brush.Color := clSilver
else
grid.Canvas.Brush.Color := clDkGray;
grid.DefaultDrawColumnCell(Rect, DataCol, Column, State) ;
end;
Probably I would need to extend the DBGrid somehow, but I do not know exactly how nor how to look for a solution for this on google
I tried to hack the DBGRid inside each form like this:
type
TDBGrid = class(DBGrids.TDBGrid)
protected
procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;Column: TColumn; State: TGridDrawState); override;
end;
...
procedure TDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;Column: TColumn; State: TGridDrawState) ;
var
grid : TDBGrid;
row : integer;
begin
row := 2;//grid.DataSource.DataSet.RecNo;
if Odd(row) then
Canvas.Brush.Color := clSilver
else
Canvas.Brush.Color := clDkGray;
DefaultDrawColumnCell(Rect, DataCol, Column, State) ;
end;
I can do this but I cannot access the sender, so I can access the dataset and know which record to color and which not (odd and even).
And this is a poor approach anyways since I will have to do it on every form, so it's not really a solution
Any ideas?
Thank you
If you put something like this in your datamodule, and assign it to the OnDrawColumnCell of every DBGrid, it seems to work (see notes that follow):
procedure TDataModule1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
RowColors: array[Boolean] of TColor = (clSilver, clDkGray);
var
OddRow: Boolean;
begin
// Safety check, although it really isn't needed; no other control accepts
// this event handler definition, AFAIK, so the only way to call it with the
// wrong Sender type would be to do so in your own code manually. In my own
// code, I'd simply leave out the check and let the exception happen; if I
// was stupid enough to do so, I'd want my hand slapped rudely.
if (Sender is TDBGrid) then
begin
OddRow := Odd(TDBGrid(Sender).DataSource.DataSet.RecNo);
TDBGrid(Sender).Canvas.Brush.Color := RowColors[OddRow];
TDBGrid(Sender).DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
end;
A couple of notes:
First, you should avoid using TDataSet.RecNo in the first place, because post-BDE datasets don't typically have this value available. Accessing it (particularly on large or query-based datasets) causes a major performance hit to your application. Of course, not using it means that you can't use this solution. A better solution would be to use a handler for the dataset's BeforeScroll or AfterScroll event that toggled a boolean available to this code instead, and use that instead of the test for Odd(RecNo), or if the dataset is only used for displaying in the DBGrid, use the TDataSet.Tag in the AfterScroll event to track the row's odd/even state using
OddRow := Boolean(DataSet.Tag);
DataSet.Tag := Ord(not OddRow);
Add DBGrids to the uses clause of your datamodule, and manually declare the above event in the published section so that it's available to all units that use the datamodule. You can then assign it in the Object Inspector Events tab as usual from those units.
This does not properly handle the TGridDrawState (nor does your initial code). You'll need to add handling for that yourself, as that wasn't what you asked here.
Depending on which color you want for odd and even rows, you may want to reverse the order of the colors in RowColors.
I prefer the repeated typecasts so that it's clear what the code is doing. If it bothers you, you can simply declare a local variable instead:
var
OddRow: Boolean;
Grid: TDBGrid;
begin
if (Sender is TDBGrid) then
begin
Grid := TDBGrid(Sender);
OddRow := Odd(Grid.DataSource.DataSet.RecNo);
...
end;
end;
This works for Delphi XE7
type
TDBGrid=Class(Vcl.DBGrids.TDBGrid)
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
end;
procedure TDBGrid.WMVScroll(var Message: TWMVScroll);
begin
Self.Invalidate;
inherited;
end;
procedure TForm1. DBGrid1MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
if Sender is TDBGrid then
(Sender as TDBGrid).Invalidate;
end;
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
MyRowColors : array[Boolean] of TColor = (clLime, clMoneyGreen);
var
RowNo : Integer;
OddRow : Boolean;
S : string;
begin
if Sender is TDBGrid then begin
with (Sender as TDBGrid) do begin
if (gdSelected in State) then begin
// Farbe für die Zelle mit dem Focus
// color of the focused row
Canvas.Brush.Color := clblue;
end
else begin
// count := trunc((Sender as TDBGrid).Height div (Rect.Bottom - Rect.Top));
// RowNo := (Sender as TDBGrid).Height div Rect.Top;
RowNo := Rect.Top div (Rect.Bottom - Rect.Top);
OddRow := Odd(RowNo);
Canvas.Brush.Color := MyRowColors[OddRow];
// Font-Farbe immer schwarz
// font color always black
Canvas.Font.Color := clBlack;
Canvas.FillRect(Rect);
// Denn Text in der Zelle ausgeben
// manualy output the text
if Column.Field <> nil then begin
S := Column.Field.AsString;
Canvas.TextOut(Rect.Left + 2, Rect.Top + 1, S);
// Canvas.TextOut(Rect.Left + 2, Rect.Top + 1, 'Column.Field.AsString');
end;
end;
end
end;
end;

Delphi: switch bands of CoolBar

What is an efficient way to change buttons (bands) in the CoolBar (the red rectangle) while switching among items in the TreeView (the purple rectangle). I want to use one set of buttons for every item in the list view.
Thanks for help and advices!
I'd create the CoolBands I'd need and assign each to the Data pointer of the TTreeNode for which it is to be used. Then in the TreeView's OnChanging handler, I'd "remember" the TreeNode that is currently selected and switch visibility on the CoolBands in the OnChange handler:
procedure TProbeerForm.TreeView1Changing(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
begin
FOldNode := TreeView1.Selected;
end;
procedure TProbeerForm.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
TCoolBand(FOldNode.Data).Visible := False;
TCoolBand(Node.Data).Visible := True;
end;

Setting multiple labels to transparent across 1.000 forms?

I skinned my software with Devexpress and I found that the labels were non-transparent causing them to have grey background.
There's just endless forms, so I was wondering whether there was a way to do this task (of setting labels to transparent) automatically.
I did a similar thing earlier, the Devexpress controls on the form had LookAndFeel.NativeStyle = True, I used Grep Search to replace it to False on all dfm forms. In the label's case however, the transparent property is not present.
Thank you.
The global Screen variable keeps track of all forms:
procedure MakeLabelsTransparent(AParent: TWinControl);
var
I: Integer;
begin
with AParent do
for I := 0 to ControlCount - 1 do
if Controls[I] is TLabel then
TLabel(Controls[I]).Transparent := True
else if Controls[I] is TWinControl then
MakeLabelsTransparent(TWinControl(Controls[I]));
end;
procedure TMainForm.ActiveFormChange(Sender: TObject);
begin
with Screen do
if (ActiveCustomForm <> nil) and (ActiveCustomForm.Tag = 0) then
begin
MakeLabelsTransparent(ActiveCustomForm);
ActiveCustomForm.Tag := 1;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Screen.OnActiveFormChange := ActiveFormChange;
end;
And if you have to use the Tag property for a particular form, then omit this check: it wouldn't really get that much slower.
For this type of task, GExperts contains the Set Component Properties tool:
This tool waits in the background
until you compile a project. It then
scans the current project's forms to
check for components with certain
properties and changes those
properties to a defined value. This
tool is useful to deactivate datasets
or database connections before you
compile your applications, but it can
be used for any similar situations as
well. To activate the scanning,
enable the checkbox next to this
expert in the GExperts Configuration
screen.
It can be used to set a property which is not yet in the DFM as well, and only requires one additional entry in the GExpert configuration, and a recompile.
I have just tested it and it works as expected.
At design time, you can just parse all .dfm then add the
Transparent = True
line just after any
object MyLabel : TLabel
line.
At runtime, you may override the TCustomForm.DoCreate and TCustomFrame.Create methods, as such:
type
THookedForm = class(TCustomForm)
procedure HookedDoCreate;
end;
THookedFrame = class(TCustomFrame)
constructor Create(AOwner: TComponent); override;
end;
var
PatchForm, OriginalForm: TPatchEvent;
PatchPositionForm: PPatchEvent = nil;
PatchFrame, OriginalFrame: TPatchEvent;
PatchPositionFrame: PPatchEvent = nil;
procedure PatchCreate;
var ov: cardinal;
begin
// hook TForm:
PatchPositionForm := PPatchEvent(#THookedForm.DoCreate);
OriginalForm := PatchPositionForm^;
PatchForm.Jump := $E9; // Jmp opcode
PatchForm.Offset := PtrInt(#THookedForm.HookedDoCreate)-PtrInt(PatchPositionForm)-5;
if not VirtualProtect(PatchPositionForm, 5, PAGE_EXECUTE_READWRITE, #ov) then
RaiseLastOSError;
PatchPositionForm^ := PatchForm; // enable Hook
// hook TFrame:
PatchPositionFrame := PPatchEvent(#TCustomFrame.Create);
OriginalFrame := PatchPositionFrame^;
PatchFrame.Jump := $E9; // Jmp opcode
PatchFrame.Offset := PtrInt(#THookedFrame.Create)-PtrInt(PatchPositionFrame)-5;
if not VirtualProtect(PatchPositionFrame, 5, PAGE_EXECUTE_READWRITE, #ov) then
RaiseLastOSError;
PatchPositionFrame^ := PatchFrame; // enable Hook
end;
{ THookedForm }
procedure THookedForm.HookedDoCreate;
var i: integer;
begin
// enumerate all labels, then set Transparent := true
for i := 0 to Components.Count-1 do
if Components[i] is TLabel then
TLabel(Components[i]).Transparent := true;
DoCreate; // call initial code
end;
{ THookedFrame }
constructor THookedFrame.Create(AOwner: TComponent);
var i: integer;
begin
// enumerate all labels, then set Transparent := true
for i := 0 to Components.Count-1 do
if Components[i] is TLabel then
TLabel(Components[i]).Transparent := true;
inherited Create(AOwner); // call normal constructor
end;
....
initialization
PatchCreate;
A related tip (I always forget to make use of this handy feature):
Configure the label the way you want to have it;
Select it on the form;
Go to Component/Create component template;
You can then a name for the template:
From then on, the template appears as a new component type in your tool palette, with the settings that you prefer.
(Yeah, I know this doesn't change current labels)
You can set the BackColor property to Color.Transparent.
The following should work: the transparent-property is present in the DFM-file only if the value is not the default. So you can us a Grep-Search to insert the "Transparent=TRUE" just in the next line after the "=TLabel". I have not tried this myself, but it is easy to try...

Resources