I am working on Delphi 10.2 and SQL Server 2008.
I have to modify some value in TDBGrid. when I modify the value using OnDrawColumnCell Data is getting over lapped when I click on that column and the same is working fine in Delphi 7.
Example Code:
Create table and insert some data in SQL Server 2008.
CREATE TABLE [dbo].[Persons](
[P_ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[LastName] [varchar](15) NOT NULL,
)
insert into Persons (LastName) values ('LastName')
Create New VCL Forms Application - Delphi
To display the Data on DBGrid I am using TADOCOnnection, TADOQuery, TDataSource and TDBGrid
set TADOQuery.SQL to "select LastName from Persons"
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, ADODB, Vcl.StdCtrls;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
procedure FormShow(Sender: TObject);
procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
ADOQuery1.Active := False;
ADOQuery1.Active := True;
end;
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
Var
CellData : String;
begin
CellData := Column.Field.DisplayText;
if Column.Field.FieldName = 'LastName' then
begin
CellData := 'change';
DBGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, CellData);
end
else
DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, state);
end;
end.
This is a general drawing issue and not related to SQL Server or the TDBGrid in particular. The same would apply to drawing to a VCL TCanvas or something similar.
Clear the area before drawing text
You are calling Canvas.TextRect(..), but nothing more. The text will get drawn, but nothing more. You will have to clear the area first: Imagine painting a white background before drawing black text.
The TDBGrid offers a convenient method DrawCellBackground(..). Since this method is not public, this screams for implementing it by leveraging helper classes.
Implementation example
The following code uses DrawCellHighlight(..) for clearing the cell paint area when the cell is selected and DrawCellBackground(..) otherwise.
type
TDBGridHelper = class helper for TDBGrid
public const textPaddingPx = 2; // Siehe TDBGrid::DefaultDrawColumnCell
public procedure writeText(
const inRect: TRect;
const text: String;
const State: TGridDrawState;
const columnIndex: Integer
);
end;
procedure TDBGridHelper.writeText(
const inRect: TRect;
const text: String;
const State: TGridDrawState;
const columnIndex: Integer
);
const
cellGridPx = 1;
var
backgroungRect: TRect;
begin
backgroungRect := inRect;
backgroungRect.Inflate(-cellGridPx, -cellGridPx);
if (Vcl.Grids.gdSelected in State) then
DrawCellHighlight(inRect, State, columnIndex, 0)
else
DrawCellBackground(backgroungRect, self.Color, State, Columnindex, 0);
Canvas.TextRect(
inRect,
inRect.Left + textPaddingPx,
inRect.Top + textPaddingPx,
text
);
end;
Leveraging the TDBGrid.OnDrawColumnCell event was absolutely correct, you can now simplify it to something like
procedure TYourFrame.dbGridDrawColumnCell(
Sender: TObject;
const Rect: TRect;
DataCol: Integer;
Column: TColumn;
State: TGridDrawState
);
var
columnText: String;
begin
columnText := '---';
if Assigned(Column.Field) then begin
if (Column.FieldName = 'yourField') then
columnText := getDeviationColumnText(Column.Field.AsSingle)
else
// This is the default text
columnText := Column.Field.DisplayText;
end;
(Sender as TDBGrid).writeText(Rect, columnText, State, Column.Index);
end;
If going through 30 forms is to tedious, you can turn off "Runtime themes" to get back that fancy Windows 2000 look and the drawing algorithms that came with it.
Choose Project > Options > Application.
Clear the Enable Runtime Themes check box.
Source: http://docwiki.embarcadero.com/RADStudio/en/Disabling_Themes_in_the_IDE_and_in_Your_Application
Related
I'm creating a derivative of the TDBGrid, and I want to implement a nicer way to define its text formatting, something similar to the GetContentStyle of the QuantumGrid.
The problem is that the DBGrid ignores the font and colors that my new event sets on its Canvas.
type TSetCellStyle = procedure(const Sender: TObject; const AColumn: TColumn; const ARow: TDataset; const AField: TField; const State: TGridDrawState; var TextFont: TFont; var BackgroundColor: TColor) of object;
TMyDBGrid = class(TDBGrid)
private
FSetCellStyle: TSetCellStyle;
protected
procedure DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); override;
published
property OnSetCellstyle: TSetCellStyle read FSetCellStyle write FSetCellStyle;
...
...
implementation
procedure TMyDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
BeginUpdate;
Canvas.Lock; // Prevents other threads from drawing on the canvas.
if Assigned(FSetCellStyle) then begin
var TextFont: TFont;
var BackgroundColor: TColor;
TextFont := Canvas.Font;
BackgroundColor := Canvas.Brush.Color;
FSetCellStyle(Self, Column, Self.DataSource.DataSet, Self.DataSource.DataSet.FindField(Column.FieldName), State, TextFont, BackgroundColor);
Canvas.Font := TextFont;
Canvas.Brush.Color := BackgroundColor;
end;
Canvas.Unlock;
inherited DrawColumnCell(Rect, DataCol, Column, State);
EndUpdate;
end;
This is an exemple of how the application uses the new event to customize the formatting of a grid:
procedure TFInspira.GridInspiraSetCellStyle(const Sender: TObject; const AColumn: TColumn; const ARow: TDataSet; const AField: TField; const State: TGridDrawState; var TextFont: TFont; var BackgroundColor: TColor);
begin
if (AColumn.FieldName = 'ReferenciaGrup') and (ARow.FieldByName('PrimerDeGrup').AsBoolean) then begin
BackgroundColor := clYellow;
end;
if ARow.FieldByName('Selected').AsBoolean then begin
TextFont.Style := TextFont.Style + [fsItalic];
end;
end;
I can debug my grid and see that the overriden DrawColumnCell sets the canvas in yellow and italic for some cells, but the Grid never shows them. Looks like the call to inherit DrawColumnCell resets the Canvas' formats.
If I can't hook my formatting event in DrawColumnCell where can I do so ?.
Thank you.
I think that your DrawColumnCell is just missing a call to DefaultDrawDataCell to get the grid to actually draw the cell. F.i.in my answer to your other q,
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
AGrid : TDBGrid;
begin
AGrid := (Sender as TDBGrid);
if Odd(AGrid.RowBeingDrawn) then begin
AGrid.Canvas.Brush.Color := clGreen;
end;
AGrid.DefaultDrawDataCell(Rect, Column.Field, State);
end;
Obviously, this paints the cells of alternate rows green.
I've used the interposer class of my other answer just so I could refer to the added RowBeingDrawn property, but code similar to the above will work just as well with the standard TDBGrid (provided its DefaultDrawing property is set to True).
I'm using LMD Innovative library in my inherited Delphi project, and in particular the TLMDGrid component.
I just want to set last row color (summary) different from the rest of the table.
I can set different colors for different columns in the designer, but (due to poor documentation) cannot find how to set color for a single row.
Thanks for help.
I don't use the LMD grids myself, so the following is based on their most recent trial download.
I wasn't sure whether you were asking about the TLMDGrid or the TLMDDbGrid so the
minimal demo below shows how to set the last row of both of them to a specific color. As you'll
see, it is just a question of setting up an OnGetCellColor event handler for each grid, and then
setting the value of the AColor variable by whatever criteria suit you.
The onGetCellColor event is passed the current column as well as the
row number of the grid cell which is about to be drawn, so this show give
you the possibility of coloring cells in the same row differently if you want.
I confess I'm not very happy with basing the test on the OnGetCellColor event of LMDDBGrid1
on the dataset's RecordCount, because not all dataset types return a meaningful value
(and for some, there can be big performance hit in getting its value. With a standard TDBGrid,
in its drawing events, you can rely on the dataset cursor being synced with the event calls (so that
any data values can be picked up from the current dataset row). I'm not sure yet about how
you would do that with the LMD grid - obviously the best place to ask about that would be LMD themselves.
Code:
unit LMDGridTestu;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DB, DBClient, Grids, LMDGrid, LMDDBGrid;
type
TForm1 = class(TForm)
CDS1: TClientDataSet;
DataSource1: TDataSource;
CDS1ID: TIntegerField;
CDS1Name: TStringField;
LMDDBGrid1: TLMDDBGrid;
LMDGridIntegerColumn1: TLMDGridIntegerColumn;
LMDGridTextColumn1: TLMDGridTextColumn;
LMDGrid1: TLMDGrid;
NameCol: TLMDGridTextColumn;
IDCol: TLMDGridIntegerColumn;
procedure FormCreate(Sender: TObject);
procedure LMDDBGrid1GetCellColor(Sender: TObject; ACellState:
TLMDGridCellStates; ARowState: TLMDGridRowState; const AData: Variant;
AColumn: TLMDGridColumn; ARow: Integer; var AColor: TColor);
procedure LMDGrid1GetCellColor(Sender: TObject; ACellState: TLMDGridCellStates;
ARowState: TLMDGridRowState; const AData: Variant; AColumn: TLMDGridColumn;
ARow: Integer; var AColor: TColor);
public
end;
[...]
procedure TForm1.LMDDBGrid1GetCellColor(Sender: TObject; ACellState:
TLMDGridCellStates; ARowState: TLMDGridRowState; const AData: Variant;
AColumn: TLMDGridColumn; ARow: Integer; var AColor: TColor);
begin
if ARow = CDS1.Recordcount - 1 then
AColor := clYellow;
end;
procedure TForm1.LMDGrid1GetCellColor(Sender: TObject; ACellState:
TLMDGridCellStates; ARowState: TLMDGridRowState; const AData: Variant;
AColumn: TLMDGridColumn; ARow: Integer; var AColor: TColor);
begin
if ARow = LMDGrid1.DataRowCount - 1 then // Rows are numbered from zero
AColor := clYellow;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i : integer;
begin
CDS1.CreateDataSet;
CDS1.IndexFieldNames := 'ID';
for i := 1 to 12 do
CDS1.InsertRecord([i, StringOfChar(Chr((ord('a') + i -1)), 20)]);
CDS1.First;
LMDGrid1.DataRowCount := 10;
for i := 0 to LMDGrid1.DataRowCount - 1 do begin
LMDGrid1.Cells[IDCol.Position, i] := IntToStr(i);
LMDGrid1.Cells[NameCol.Position, i] := 'Name' + IntToStr(i);
end;
end;
We are moving our application from XE6 to 10.1 Berlin and we have a custom grid style not working. As I test, I went back to XE6 to look at the default gridstyle & checkcellstyle when using a TCheckcolumn and it works fine. I then went to 10.1 Berlin and tested the default gridstyle and checkcellstyle when using a TCheckcolumn and it does NOT work. i.e., the checkbox does not show unless you click on the row in grid....and after it does show, you can't check or uncheck it. Is this a bug or is there something else we need to do in 10.1 Berlin?
The difference between XE6 FMX.Grid and Delphi 10.1 Berlin FMX.Grid, specifically the TCheckColumn, is that in XE6 the checkboxes are visible even if the linked TValue is Empty. This was the case up until D10 Seattle. In D10.1 Berlin this was changed so, that the checkboxes are visible only if the linked TValue is either False or True. If TValue is Empty the checkbox is not shown.
In D10.1 Berlin the following test (similar to the document example) works for me:
type
TForm27 = class(TForm)
Grid1: TGrid;
Column1: TColumn;
CheckColumn1: TCheckColumn;
StringColumn1: TStringColumn;
procedure Grid1GetValue(Sender: TObject; const ACol, ARow: Integer;
var Value: TValue);
procedure Grid1SetValue(Sender: TObject; const ACol, ARow: Integer;
const Value: TValue);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
Arr: array of TValue;
public
{ Public declarations }
end;
implementation
procedure TForm27.FormCreate(Sender: TObject);
begin
SetLength(Arr, Grid1.RowCount);
end;
procedure TForm27.Grid1GetValue(Sender: TObject; const ACol, ARow: Integer;
var Value: TValue);
begin
if ACol = 1 then
Value := Arr[ARow];
end;
procedure TForm27.Grid1SetValue(Sender: TObject; const ACol, ARow: Integer;
const Value: TValue);
begin
if ACol = 1 then
Arr[ARow] := Value.AsBoolean;
end;
And at runtime it looks like this:
Here I have visited three rows and they show the checkboxes.
And for comparison the XE6 test;
I am using delphi 2010 for a project with a stringgrid. I want some columns of the grid to be right justified. I understand how I can do this with defaultdrawing set to false.
I would like, however, to keep the runtime theme shading for the grid, if possible. Is there a way to right justify a column with defaultdrawing enabled, or at least duplicate the code in the onDrawCell event to imitate the runtime theme shading?
you can use an interposer class and override the DrawCell method, check this sample
type
TStringGrid = class(Grids.TStringGrid)
protected
procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
end;
TForm79 = class(TForm)
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);
private
end;
var
Form79: TForm79;
implementation
{$R *.dfm}
{ TStringGrid }
procedure TStringGrid.DrawCell(ACol, ARow: Integer; ARect: TRect; AState: TGridDrawState);
var
s : string;
LDelta : integer;
begin
if (ACol=1) and (ARow>0) then
begin
s := Cells[ACol, ARow];
LDelta := ColWidths[ACol] - Canvas.TextWidth(s);
Canvas.TextRect(ARect, ARect.Left+LDelta, ARect.Top+2, s);
end
else
Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, Cells[ACol, ARow]);
end;
procedure TForm79.FormCreate(Sender: TObject);
begin
StringGrid1.Cells[0,0]:='title 1';
StringGrid1.Cells[1,0]:='title 2';
StringGrid1.Cells[2,0]:='title 3';
StringGrid1.Cells[0,1]:='normal text';
StringGrid1.Cells[1,1]:='right text';
StringGrid1.Cells[2,1]:='normal text';
end;
And the result
Well I have an issue with DBGrid vertical scrolling. When I scroll it vertically with mousewheel or vertical scrollbar it moves selected row up and down. I want to make it scroll not selected row but entire grid. Just like it works in Microsoft Excel for example (just to let you know what I mean). Any suggestions?
Well, almost what I'd like to see. Found the post of hanuleye on swissdelhicenter.ch. This code let's you freely scroll DBGrid with mouse wheel.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, DBTables;
type
TForm1 = class(TForm)
DataSource1: TDataSource;
Table1: TTable;
DBGrid1: TDBGrid;
procedure FormCreate(Sender: TObject);
procedure DBGridMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
TWheelDBGrid = class(TDBGrid)
public
property OnMouseWheel;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TWheelDBGrid(DBGrid1).OnMouseWheel := DBGridMouseWheel;
end;
function GetNumScrollLines: Integer;
begin
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, #Result, 0);
end;
procedure TForm1.DBGridMouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
Direction: Shortint;
begin
Direction := 1;
if WheelDelta = 0 then
Exit
else if WheelDelta > 0 then
Direction := -1;
with TDBGrid(Sender) do
begin
if Assigned(DataSource) and Assigned(DataSource.DataSet) then
DataSource.DataSet.MoveBy(Direction * GetNumScrollLines);
Invalidate;
end;
end;
end.
I don't think that's possible, since to me it seems that scrollbar on DBGrids are more like a progress indicator rather than a scroll. It behaves differently from the scrolls in ListViews where you scroll "pages", in the db controls even if you move up or down a single row the scrollbar changes to reflect the "current row"/"total rows" fraction