I have an 8 x 16 DrawGrid in Delphi XE5 that I would like to randomly fill with nine images I've stored in C:\Users\Sean Ewing\Documents\My Documents\Delphi Tutorials\Other\Math-O-Sphere\Win32\Debug\img. I'm currently trying to get one image to load to make sure I'm doing it correctly. Here is the code I've used to do this:
procedure TForm1.grdPlayFieldDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
spherePlus: TBitmap;
begin
spherePlus.LoadFromFile(ExtractFilePath(Application.ExeName) + '\img\Sphere +1.bmp');
grdPlayField.Canvas.Draw(0, 0, spherePlus);
end;
The code compiles fine, and based on what I've read in the Embarcadero wiki this is correct, but I get an error at runtime when it's time to load the DrawGgrid. Where did I go wrong?
You need to first create the bitmap before you can use it:
procedure TForm1.grdPlayFieldDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
spherePlus: TBitmap;
begin
spherePlus := TBitmap.Create;
try
spherePlus.LoadFromFile(ExtractFilePath(Application.ExeName) +
'\img\Sphere +1.bmp');
grdPlayField.Canvas.Draw(0, 0, spherePlus);
finally
spherePlus.Free;
end;
end;
The other thing you should be aware of is that the Rect parameter you receive in the event is the area that needs to be painted, so you'll want to use Canvas.StretchDraw and pass it that rectangle. It won't help with the current issue, but you'll need it when you move to the next step. You can identify the exact cell that's being drawn with the ACol and ARow parameters, so you can use that information to load a specific image for a column, for instance, or to output text for a column or row.
// Load specific image for the cell passed in ACol and ARow,
// and then draw it to the appropriate area using the Rect provided.
grdPlayField.Canvas.StretchDraw(Rect, spherePlus);
Related
I'm using the following code in the CustomCellDraw event of DBAdvGrid(TMS) to increase row height.
procedure TForm1.DBAdvGrid1CustomCellDraw(Sender: TObject; Canvas: TCanvas;
ACol, ARow: Integer; AState: TGridDrawState; ARect: TRect; Printing: Boolean);
begin
DBAdvGrid1.RowHeights[ARow]:=120;
end;
How do I make it avoid increasing row 0, which is the 1st row in the Grid, containing column names/headers? - I'd like that row to remain untouched while all the rest should get resized via the above code. Basically it should ignore row index 0 and start from row index 1
It would be like this:
procedure TForm1.DBAdvGrid1CustomCellDraw(Sender: TObject; Canvas: TCanvas;
ACol, ARow: Integer; AState: TGridDrawState; ARect: TRect; Printing: Boolean);
begin
if ARow > 0 then
DBAdvGrid1.RowHeights[ARow] := 120;
end;
But do not modify row heights from a drawing event. Such event is triggered frequently, and is used exclusively for content painting, not for adjusting content size. What's worse, if you e.g. allowed row sizing and the user would try to setup row height, it would in turn trigger that event where you would change the height back, so you'd be fighting with the user.
Content sizing should be done earlier, as this example shows in the OnCustomCellSize event.
But for your aim I think it's enough to set DefaultRowHeight and FixedRowHeight properties with no additional code.
Problem using image from a TImage list to draw a glyph on to a data cell in DBGrid:
I am putting a bmp image of a "checkmark" in place of the text "Done" in a particular data cell. It works, but there is always black color in the parts of the cell not covered by the image. I have tried enlarging the pixel size of the bmp image to match the cell size, but it always seems to resize the image for me. Using Delphi 10.2, was not problem in D7?
Have tried many combos of setting background colors, pen and brush colors, etc. Here is a simple example of one code attempt:
procedure TFUpRepWS.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
with Column do begin
if ((FieldName = 'Done') and (Field.AsString = 'x')) then begin
//below shows black outside of check mark image in the cell
ImageList1.Draw(DBGrid1.Canvas,Rect.Left,Rect.Top,0)
end
else DBGrid1.DefaultDrawColumnCell(Rect,DataCol,Column,State);
end;
end;
Do the default cell painting DefaultDrawColumnCell always. That will ensure the cell will look like the others. Then draw the image. Try this:
procedure TFUpRepWS.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
with Column do
begin
DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
if ((FieldName = 'Done') and (Field.AsString = 'x')) then
ImageList1.Draw(DBGrid1.Canvas, Rect.Left, Rect.Top, 0);
end;
end;
I guess that what you described happens because there is no code that paints the cell background.
I have a problem, I have an TStringList object with some numbers and his status in some kind of json text I created, like: {'8987436','Sin documentar.', '0','1'}, {...},...
This file can contain a lot of groups of data. This works really fine, this information I displayed in a DBGrid, only the numbers.
The problem is that when I try to change the color of the rows, only draw the color of the last number added to the dbgrid, and I need that each row with some type of number draw in yellow, other kind in red, other kind in green and all the other let it white. The other problem is that when I click on one row it refresh the dbgrid and paint it again in white.
I created this procedure:
procedure TEmbarqueGeneracionEscaneoFRM.DBValidasDrawColumnCell(
Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
inherited;
if Pinta then
begin
with (Sender As TDBGrid).Canvas do
begin
brush.Color:=GridColor;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, Column.Field.AsString);
end;
TDBGrid(sender).DefaultDrawColumnCell(Rect, Datacol, Column, State);
end;
Pinta := false;
end;
Where Pinta is a variable that tell if the number was painted, and GridColor is a TColor variable with the color that will be drawn.
Did you have any idea?
Regards
I am using Developer Express components - TdxDBgrid as Grid and TdxMemData as dataset.
There are around 10 columns displayed in grid.
For the second column, I am trying to change starting point where column value displayed. I am trying to do it using ACanvas.TextRect. But the changes are not getting affected. Anybody having idea how to change starting position of data in Column for any grid.
thanks
I'm not sure if what you're asking is how to shift the x-position where the text starts, but if it is, try something like this:
procedure TForm1.dxDBGrid1Column2CustomDrawCell(Sender: TObject;
ACanvas: TCanvas; ARect: TRect; ANode: TdxTreeListNode;
AColumn: TdxTreeListColumn; ASelected, AFocused, ANewItemRow: Boolean;
var AText: String; var AColor: TColor; AFont: TFont;
var AAlignment: TAlignment; var ADone: Boolean);
var
XOffset : Integer;
begin
XOffset := 20;
ACanvas.FillRect(ARect);
ACanvas.TextOut(ARect.Left + XOffset, ARect.Top, AText);
ADone := True;
end;
Obviously that doesn't deal with details like how to draw selected and focused columns, etc, but you should get the idea and you can look at the DevEx source for those.
I am using the version of TeeChart that ships with Rad Studio XE3.
TeeChart provides a TChartSeries event which fires when the mouse pointer moves over a series line. I use this event to display the name of the series under the pointer.
The problem is, give a series line 1 pixel wide, it’s difficult to get the pointer exactly over the line. Is there some way to add ‘padding’ to the event so it fires X number of pixels to each side of the line?
Or is there some other way to accomplish this?
I'm adding a new property to Line (TLineSeries) and FastLine (TFastLineSeries) classes to accomplish this.
Series1.ClickTolerance := 4; // <-- number of pixels around mouse XY
The default value is zero (mouse XY should be exactly over the line), like the current behavior.
As a workaround, if you are using TLineSeries, pointers can be displayed at line point positions, and the internal "clicked" function will consider pointer size:
Series1.Pointer.Visible:=True;
And for more custom control, the code below is very similar to the internal code use to detect mouse clicks. The Tolerance constant specifies the number of extra pixels to consider "in the line".
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
const
Tolerance=4;
var Clicked,
t : Integer;
Position,
P,Old : TPoint;
begin
Clicked:= -1;
Position.X:=X;
Position.Y:=Y;
for t:=Series1.FirstValueIndex to Series1.LastValueIndex do
begin
P.X:=Series1.CalcXPos(t);
P.Y:=Series1.CalcYPos(t);
if t>Series1.FirstValueIndex then
if PointInLine(Position,P.X,P.Y,Old.X,Old.Y,Tolerance) then
begin
Clicked:=t;
break;
end;
Old:=P;
end;
if Clicked = -1 then
Caption:=''
else
Caption:=IntToStr(Clicked);
end;
You can use the PointInLineTolerance function to check it at OnMouseMove event.
However, you have to loop the series points manually to transform the series values into pixels and pass them to this function.
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=false;
for i:=0 to 5 do
Chart1.AddSeries(TLineSeries).FillSampleValues;
end;
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var series, valueIndex: Integer;
P0, P1: TPoint;
begin
Chart1.Draw;
for series:=0 to Chart1.SeriesCount-1 do
with Chart1[series] do
for valueIndex:=FirstValueIndex to LastValueIndex-1 do
begin
P0.X:=CalcXPos(valueIndex);
P0.Y:=CalcYPos(valueIndex);
P1.X:=CalcXPos(valueIndex+1);
P1.Y:=CalcYPos(valueIndex+1);
if PointInLineTolerance(Point(X, Y), P0.X, P0.Y, P1.X, P1.Y, 5) then
begin
Chart1.Canvas.TextOut(X+5,Y-10,'Series ' + IntToStr(series));
exit;
end;
end;
end;