I want to create a different Popup for each column. Since the columns order can be changed I need to identify that by the column title but I have not found a solution.
These are two method I have applied without success.
procedure TForm2.DBGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
ACol, ARow: Integer;
begin
if Y < DBGrid1.DefaultRowHeight then
begin
(Sender as TDBGrid).MouseToCell(X, Y, ACol, ARow);
if Button = mbRight then
begin
if DBGrid1.SelectedColumn.FieldName = 'Title1' then
BEGIN
ShowMessage('Title1'+ IntToStr(ACol));
end;
if DBGrid1.SelectedColumn.FieldName = 'Title2' then
BEGIN
ShowMessage('Title2'+ IntToStr(ACol));
end;
end;
end;
end;
It is not functional because it identifies the columns by id and not the name so if the user change the columns order it will not work fine.
Also this faily code
procedure TForm2.Button2Click(Sender: TObject);
var
i: Integer;
CaptionText: string;
begin
for i := 0 to DBGrid1.Columns.Count - 1 do
case DBGrid1.Columns[i].FieldName of
'TEST':
begin
DBGrid1.Columns[i].Title.Caption := 'REPLACE TEXT';
end;
end;
end;
It simply replace all titles in a click.
What I'm looking to do is to create an IF like this that handle on right clicking column title:
if selected column name = 'test' then
begin
showmessage('You have selected test column');
end;
I'll use the if to create dynamic popup to apply filters.
The code below is an event handler for a TDBGrid's MouseMove event which
displays the Caption of the Title of the column the mouse is over on the caption of the form.
procedure TForm1.DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
Col,
Row : Integer;
begin
Col := DBGrid1.MouseCoord(X, Y).X;
Row := DBGrid1.MouseCoord(X, Y).Y;
if (dgIndicator in DBGrid1.Options) then
Dec(Col);
if (Col >= 0) and (Col < DBGrid1.Columns.Count) then
Caption := DBGrid1.Columns[Col].Title.Caption
else
Caption := '';
end;
Note that the
if (dgIndicator in DBGrid1.Options) then
Dec(Col);
is to adjust the behaviour for correct operation if the dgIndicator option is turned off.
Obviously, instead of
Caption := DBGrid1.Columns[Col].Title.Caption
you could do
MenuItem.Caption := DBGrid1.Columns[Col].Title.Caption
to copy the column title to a menuitem's caption. I think you probably don't need to do any comparison of the Title.Caption's value with any hard-coded constants, but obviously that's your choice.
Btw, if you would rather access the name of the dataset field which is supplying the displayed values for the Column's contents, you can read the Column's FieldName property.
Related
I am having problems with adding text I have entered into a tedit, into an record.
Here is the code i currently have:
procedure TForm7.AddNewQuestionClick(Sender: TObject);
var
w: integer;
QuestDesc, QuestAnsr: string;
begin
NewQuestID.text:=(GetNextQuestionID); //Increments QuestionID part of record
w:=Length(TQuestions);
SetLength(TQuestions, w+1);
QuestDesc:= NewQuestDesc.text;
QuestAnsr:= NewQuestAns.text;
TQuestionArray[w+1].Question:= QuestDesc; // Error on this line (No default property available)
TQuestionArray[w+1].Answer:= QuestAnsr;
end;
Here is the record I am trying to add to:
TQuestion = record
public
QuestionID: integer;
Question: shortstring;
Answer: shortstring;
procedure InitQuestion(anID:integer; aQ, anA:shortstring);
end;
TQuestionArray = array of TQuestion;
Any help solving this problem would be greatly appreciated.
You're missing a few things. You've declared a procedure to help initialize a new question - you should be using it.
This should get you going:
type
TQuestion = record
QuestionID: integer;
Question: ShortString;
Answer: ShortString;
procedure InitQuestion(anID: Integer; aQ, aAns: ShortString);
end;
TQuestionArray = array of TQuestion;
var
Form3: TForm3;
var
Questions: TQuestionArray;
procedure TForm7.AddNewQuestionClick(Sender: TObject);
begin
SetLength(Questions, Length(Questions) + 1);
Questions[High(Questions)].InitQuestion(GetNextQuestionID,
NewQuestDesc.Text,
NewQuestAns.Text);
end;
If you really want to do it individually setting the fields:
procedure TForm7.AddNewQuestionClick(Sender: TObject);
var
Idx: Integer;
begin
SetLength(Questions, Length(Questions) + 1);
Idx := High(Questions);
Questions[Idx].QuestionID := GetNextQuestionID;
Questions[Idx].Question := NewQuestDesc.Text;
Questions[Idx].Answer := NewQuestAns.Text;
end;
I need to re-order a TObjectList, according to some rules. How can I achieve this?
So I add panels to a ScrollBox dinamically.
When I add them, I also add them to the ObjectList in the order that they are added at runtime, for future use. Then I can re-organize the panels in the scrollBox by drag/drop.
I want the ObjectList to mirror the same order that is set at runtime by drag/drop.
Here is my code:
var
MainForm: TMainForm;
PanelList,PanelListTMP:TObjectList;
implementation
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
PanelList:=TObjectList.Create;
PanelListTMP:=TObjectList.Create;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
AddPanel('0');
AddPanel('1');
AddPanel('2');
AddPanel('3');
AddPanel('4');
end;
procedure TMainForm.Addpanel(what:string);
var
pan:TPanel;
bv:TShape;
begin
pan:=TPanel.Create(self);
pan.Parent:=TheContainer;
pan.Height:=50;
pan.BevelOuter:=bvNone;
pan.BorderStyle:=bsNone;
pan.Ctl3D:=false;
pan.Name:='LayerPan'+what;
pan.Caption:=what;
pan.Align:=alBottom;
pan.OnMouseDown:=panMouseDown;
end;
procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
var
i:integer;
idu:String;
panui:TPanel;
begin
panui:=Sender as TPanel;
panui.ParentColor:=false;
panui.BringToFront;
// DRAG DROP STUFF
ReleaseCapture;
panui.Perform(wm_nclbuttondown,HTCAPTION,0);
for i := 0 to MainForm.ComponentCount - 1 do
begin
if MainForm.Components[i] is TWinControl then
if TWinControl(MainForm.Components[i]) is TPanel then
if (TWinControl(MainForm.Components[i]) as TPanel).Parent=MainForm.TheContainer then
begin
(TWinControl(MainForm.Components[i]) as TPanel).Align:=alBottom;
end;
end;
TheContainer.ScrollInView(panui);
ReOrderPanels;
end;
Procedure TMainForm.ReOrderPanels;
begin
end;
What should I do in the ReOrderPanels procedure?
I was thinking about feeding the panels of the ScrollBox from bottom to top into a new TObjectList (PanelListTMP), clear the PanelList and re-add them from the PanelListTMP, but when I do that, I get an error: Access Violation, and EInvalidPointer - Invalid Pointer Operation
So this is what I thought:
procedure TMainForm.ReOrderPanels;
var
ctrl:TControl;
pos:TPoint;
pan:TPanel;
bad:boolean;
ord,i:integer;
begin
memo2.Lines.Add('*** new order START');
panelListTMP.Clear;
// scroll top
TheContainer.VertScrollBar.Position := 0;
// scroll down
TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range;
// get panel
Pos:=TheContainer.ClientOrigin;
Pos.Y:=Pos.Y+TheContainer.Height-5;
ctrl := FindVCLWindow(pos) ;
if ctrl is TPanel then
if TPanel(ctrl).Parent = TheContainer then
begin
pan:=(ctrl as TPanel);
panelListTMP.Add(pan);
end;
ord:=1;
bad:=false;
repeat
repeat
Pos.Y:=pos.Y-1;
until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan);
if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then
begin
pan:=FindVCLWindow(pos) as TPanel;
containeru.VertScrollBar.Position := 0;
containeru.ScrollInView(pan);
ord:=ord+1;
panelListTMP.Add(pan);
end
else
bad:=true;
until bad=true;
// and now I do the swap between the ObjectLists...
panelList.Clear;
for i:=0 to PanelListTMP.Count-1 do
begin
(PanelListTMP.Items[i] as TPanel).Parent:=containeru;
panelList.Add(PanelListTMP.Items[i]);
end;
end;
So I assume that because the ObjectList is storing pointers to the actual objects, then when I clear the initial ObjectList, the actual objects are freed, so the second ObjectList contains a list of pointers that are no longer viable...
But then how can I achieve what I want?
So on ButtonClick, I get a ObjectList that contains panels in the following order:
PanelList[0] - Panel0
PanelList[1] - Panel1
PanelList[2] - Panel2
PanelList[3] - Panel3
PanelList[4] - Panel4
After I drag - drop panels inside the ScrollBox, I can end up with an order like this (in the ScrollBox)
Panel3
panel1
Panel4
Panel2
Panel0
But in the ObjectList, the order is the same as before...
Again, I want to be able to have the ObjectList ordered according to the order of the panels from the scrollBox.
In the re-order procedure I actually get all the panels in the desired order.
I just need to have them in the same order in my ObjectList.
Is there any other way of doing this? Other that with me creating a new class that would hold an index beside a TPanel and use that in the ObjectList to maintain the order?
TObjectList has an OwnsObjects property that is True by default. Make sure to set it to False since you don't want the list to auto-free the objects as they are owned by the Form.
As for the actual sorting of the TObjectList, consider using its Sort() or SortList() method for that. After you have repositioned the Panels as desired within their container, call Sort() or SortList(). The sorting callback you provide will be given two object pointers at a time while the sorting is iterating the list. Use the current positions of the objects relative to each other to tell the list what order they should appear in.
Try something like this:
var
MainForm: TMainForm;
PanelList: TObjectList;
implementation
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
PanelList := TObjectList.Create(False);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
PanelList.Free;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
AddPanel('0');
AddPanel('1');
AddPanel('2');
AddPanel('3');
AddPanel('4');
end;
procedure TMainForm.Addpanel(what: string);
var
pan: TPanel;
bv: TShape;
begin
pan := TPanel.Create(Self);
try
pan.Parent := TheContainer;
pan.Height := 50;
pan.BevelOuter := bvNone;
pan.BorderStyle := bsNone;
pan.Ctl3D := false;
pan.Name := 'LayerPan'+what;
pan.Caption := what;
pan.Align := alBottom;
pan.OnMouseDown := panMouseDown;
PanelList.Add(pan);
except
pan.Free;
raise;
end;
end;
procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
idu: String;
panui, pan: TPanel;
tmpList: TObjectList;
begin
panui := Sender as TPanel;
panui.ParentColor := false;
panui.BringToFront;
// DRAG DROP STUFF
ReleaseCapture;
panui.Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0);
tmpList := TObjectList.Create(False);
try
for i := 0 to TheContainer.ControlCount - 1 do
begin
if TheContainer.Controls[i] is TPanel then
tmpList.Add(TPanel(TheContainer.Controls[i]));
end;
for i := 0 to tmpList.Count - 1 do
TPanel(tmpList[i]).Align := alBottom;
finally
tmpList.Free;
end;
TheContainer.ScrollInView(panui);
ReOrderPanels;
end;
function SortPanels(Item1, Item2: Pointer): Integer;
begin
Result := TPanel(Item2).Top - TPanel(Item1).Top;
end;
procedure TMainForm.ReOrderPanels;
begin
PanelList.Sort(SortPanels);
// Alternatively:
{
PanelList.SortList(
function(Item1, Item2: Pointer): Integer;
begin
Result := TPanel(Item2).Top - TPanel(Item1).Top;
end
);
}
end;
I think I found my answer using a temporary ObjectList and Extract(Object)
My code that seems to work is:
procedure TMainForm.ReOrderPanels;
var
ctrl:TControl;
pos:TPoint;
pan,panx:TPanel;
bad:boolean;
ord,i:integer;
begin
panelListTMP.Clear;
panelList.OwnsObjects:=false;
// scroll top
TheContainer.VertScrollBar.Position := 0;
// scroll down
TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range;
// get panel
Pos:=TheContainer.ClientOrigin;
Pos.Y:=Pos.Y+TheContainer.Height-5;
ctrl := FindVCLWindow(pos) ;
if ctrl is TPanel then
if TPanel(ctrl).Parent = TheContainer then
begin
pan:=(ctrl as TPanel);
panelListTMP.Add(PanelList.Extract(pan) as TPanel);
end;
ord:=1;
bad:=false;
repeat
repeat
Pos.Y:=pos.Y-1;
until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan);
if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then
begin
pan:=FindVCLWindow(pos) as TPanel;
TheContainer.VertScrollBar.Position := 0;
TheContainer.ScrollInView(pan);
ord:=ord+1;
panelListTMP.Add(PanelList.Extract(pan) as TPanel);
end
else
bad:=true;
until bad=true;
panelList.Clear;
panelListTMP.OwnsObjects:=false;
i:=0;
while (PanelListTMP.Count<>0) do
panelList.Add(PanelListTMP.Extract(PanelListTMP.Items[i]) as TPanel);
panelList.OwnsObjects:=true;
panelListTmp.Clear;
end;
I'm kind of new to delphi graphics methods and I'm stucked at creating a ... viewport , thats how I call it while i was doing it for a project. I'm sorry I can't provide any code for it but I'm stuck at the logic part , searching google pointed me to some OnPaint , Draw methods. But those are not what I'm trying to accomplish, since I have , for example:
A 1600x1000 background image anchored to the client's top/bottom/right and left.
Multiple TImage elements placed at set x/y coords.
A "hotspot" like a map element in HTML where I can set the clickable areas (for the images i'm placing at step 2)
No zoom needed.
And the most important thing, while the background is dragged, those TImages placed on top of the background need to be dragged too.
My logic (in HTML/jQuery) was to create a #viewportBinder (which was the div i was dragging, transparent bg), followed by another div inside it called #viewtown (1600x1000, the background) which contains the divs (those TImages) placed at set coordinates in CSS.
So when I am dragging the viewportBinder, jQuery sets the new x/y on the #viewport. Implicitly, the divs (TImages) inside the #viewport are moving because the parent was positioned relative.
Does anybody have any experience with this kind of project ? Any snippet of code ?
To be more specific i'll give you my html example of what i accomplised and what i want to port into Delphi code: http://www.youtube.com/watch?v=9iYqzvZFnGA
Sorry if i'm not clear enough, i have no starting point since I have no experience with this in delphi at all. (using RAD Studio 2010)
A very short example how it could be realized in an easy way.
You would use a Paintbox for painting, 1 Backimage, an array of Records with info and transparent pngimages.
Canvas can be manipulated in offset/zoom/rotation.
Moving and hitdetection would happen in mousedown and mousemove.
It's not complete, but might give you an idea how it could be done.
[delphi]
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls,PNGImage, StdCtrls;
type
TBuilding=Record // record for building informations
Pos:TPoint;
PNGImage:TPngImage;
// what ever needed
End;
TBuildingArray=Array of TBuilding; // array of buildings
TForm1 = class(TForm)
PaintBox1: TPaintBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private-Deklarationen }
FXoffs,FYOffs,FZoom:Double; // offset and zoom for painting
FMouseDownPoint:TPoint;
FBackGroundPNG:TPNGImage;
FBuildingArray:TBuildingArray;
procedure Check4Hit(X, Y: Integer);
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
uses Math;
{$R *.dfm}
Procedure SetCanvasZoomAndRotation(ACanvas:TCanvas;Zoom:Double;Angle:Double;CenterpointX,CenterpointY:Double);
var
form : tagXFORM;
Winkel:Double;
begin
Winkel := DegToRad(Angle);
SetGraphicsMode(ACanvas.Handle, GM_ADVANCED);
SetMapMode(ACanvas.Handle,MM_ANISOTROPIC);
form.eM11 := Zoom * cos( Winkel);
form.eM12 := Zoom *Sin( Winkel) ;
form.eM21 := Zoom * (-sin( Winkel));
form.eM22 := Zoom * cos( Winkel) ;
form.eDx := CenterpointX;
form.eDy := CenterpointY;
SetWorldTransform(ACanvas.Handle,form);
end;
Procedure ResetCanvas(ACanvas:TCanvas);
begin
SetCanvasZoomAndRotation(ACanvas , 1, 0, 0,0);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Path:String;
i:Integer;
begin
FZoom := 1;
DoubleBuffered := true;
Path := ExtractFilePath(Paramstr(0));
FBackGroundPNG:=TPNGImage.Create;
FBackGroundPNG.LoadFromFile(Path + 'infect.png');
SetLength(FBuildingArray,3);
for I := 0 to High(FBuildingArray) do
begin
FBuildingArray[i].PNGImage := TPngImage.Create;
FBuildingArray[i].PNGImage.LoadFromFile(Path + Format('B%d.png',[i]));
FBuildingArray[i].Pos.X := I * 300;
FBuildingArray[i].Pos.Y := Random(1000);
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i:Integer;
begin
for I := 0 to High(FBuildingArray) do
begin
FBuildingArray[i].PNGImage.Free;
end;
FBackGroundPNG.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if FZoom=0.5 then FZoom := 1 else FZoom := 0.5;
PaintBox1.Invalidate;
end;
procedure TForm1.Check4Hit(X,Y:Integer);
var
i,Index:Integer;
R:TRect;
P:TPoint;
begin
index := -1;
for I := 0 to High(FBuildingArray) do
begin
R := Rect(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y
,FBuildingArray[i].Pos.X + FBuildingArray[i].PNGImage.Width
,FBuildingArray[i].Pos.Y + FBuildingArray[i].PNGImage.Height);
P := Point(Round((x - FXOffs)/FZoom) ,Round((y - FYOffs)/FZoom));
if PtInRect(R,P) then Index := i;
end;
if index > -1 then
begin
Caption := Format('Last hit %d',[index]);
end
else Caption := 'No Hit';
end;
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Check4Hit(X,Y);
FMouseDownPoint.X := X;
FMouseDownPoint.Y := Y;
end;
procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if ssLeft in Shift then
begin
FXoffs := -( FMouseDownPoint.X - X) ;
FYoffs := -( FMouseDownPoint.Y - Y) ;
if FXoffs>0 then FXoffs := 0;
if FYoffs>0 then FYoffs := 0;
PaintBox1.Invalidate;
end;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
i:Integer;
begin
SetCanvasZoomAndRotation(PaintBox1.Canvas,FZoom,0,FXoffs,FYOffs);
PaintBox1.Canvas.Draw(0,0,FBackGroundPNG);
for I := 0 to High(FBuildingArray) do
begin
PaintBox1.Canvas.Draw(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y,FBuildingArray[i].PNGImage);
end;
end;
end.
[/delphi]
Sorry, but for last several years i working with Lazarus instead of Delphi. But tis article will be informative: http://wiki.lazarus.freepascal.org/Developing_with_Graphics#Create_a_custom_control_which_draws_itself
About relative coordinates nothing to say - it is simple.
About dragging: A long time ago in a galaxy far, far away.. that was something like:
// To start dragging
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
// To stop dragging
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
// To perform dragging
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
I am running Lazarus v0.9.30 (32 bit compiler).
I have a TForm with a standard TStringGrid on it. The grid has the following properties set. RowCount = 5, ColumnCount = 5, FixedCols = 0, FixedRows = 0.
I Googled some code that showed me how to change the cell colour and add some text to the cell when a user clicks on a TStringGrid cell. All works fine and I have extended it slightly to toggle the color/text on and off on the GridClick event.
The questions I have are more to better understand the purpose behind some of the elements of the code.
There is an array of Foregroud (FG) and Background (BG) TColor objects. Are they there to store the cell color attributes that are set on the GridClick event, so if the DrawCell event needs to get triggered again for any reason the cell can redraw itself? Can you avoid using the array of TColors and just set the colour / text in the DrawCell event as required?
If you need to use the arrays, I would assume that dimensions must match the Grid.ColCount and Grid.RowCount (ie. set via the SetLength call in Form.Create)
Is there a way to detect that you are clicking outside of the 5 x 5 cells of the stringgrid (ie in the whitespace) and thus prevent the GridClick from calling the DrawCell event. No matter where you click you always get a valid value for Row and Col.
unit testunit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
ExtCtrls, Menus, ComCtrls, Buttons, Grids, StdCtrls, Windows, Variants,
LCLType;
type
{ TForm1 }
TForm1 = class(TForm)
Grid: TStringGrid;
procedure FormCreate(Sender: TObject);
procedure GridClick(Sender: TObject);
procedure GridDrawCell(Sender: TObject; aCol, aRow: Integer;
aRect: TRect; aState: TGridDrawState);
end;
var
Form1: TForm1;
implementation
var
FG: array of array of TColor;
BG: array of array of TColor;
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
var
Col, Row: integer;
begin
// Set the sizes of the arrays
SetLength(FG, 5, 5);
SetLength(BG, 5, 5);
// Initialize with default colors
for Col := 0 to Grid.ColCount - 1 do begin
for Row := 0 to Grid.RowCount - 1 do begin
FG[Col, Row] := clBlack;
BG[Col, Row] := clWhite;
end;
end;
end;
procedure TForm1.GridDrawCell(Sender: TObject; aCol, aRow: Integer;
aRect: TRect; aState: TGridDrawState);
var
S: string;
begin
S := Grid.Cells[ACol, ARow];
// Fill rectangle with colour
Grid.Canvas.Brush.Color := BG[ACol, ARow];
Grid.Canvas.FillRect(aRect);
// Next, draw the text in the rectangle
Grid.Canvas.Font.Color := FG[ACol, ARow];
Grid.Canvas.TextOut(aRect.Left + 22, aRect.Top + 2, S);
end;
procedure TForm1.GridClick(Sender: TObject);
var
Col, Row: integer;
begin
Col := Grid.Col;
Row := Grid.Row;
// Set the cell color and text to be displayed
if (Grid.Cells[Col,Row] <> 'Yes') then
begin
BG[Col, Row] := rgb(131, 245, 44);
FG[Col, Row] := RGB(0, 0, 0);
Grid.Cells[Col, Row] := 'Yes'
end {if}
else
begin
BG[Col, Row] := rgb(255, 255, 255);
FG[Col, Row] := RGB(255, 255, 255);
Grid.Cells[Col, Row] := '';
end; {else}
end;
end.
If you set the AllowOutboundEvents to False, the OnClick event will be fired only when you click on a certain cell, not when you click on a whitespace. So if you use this property, you will always get valid cell coordinates when you click somewhere.
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1.AllowOutboundEvents := False;
...
end;
Another point is that you should use the OnPrepareCanvas event instead of OnDrawCell, because in OnDrawCell you would have to paint everything, including text rendering. With OnPrepareCanvas you just set the Brush.Color and Font.Color for each cell which is going to be rendered.
And, you don't need to use arrays, you can use the Objects like you did with your columns, but surely you can keep the colors in the arrays. In the following example I've used the Objects and there's also shown the usage of the OnPrepareCanvas event, but note that this example as well as yours from the question colorize all cells including the fixed ones:
type
TCellData = class(TObject)
private
FStateYes: Boolean;
FForeground: TColor;
FBackground: TColor;
public
property StateYes: Boolean read FStateYes write FStateYes;
property Foreground: TColor read FForeground write FForeground;
property Background: TColor read FBackground write FBackground;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Col, Row: Integer;
CellData: TCellData;
begin
for Col := 0 to StringGrid1.ColCount - 1 do
for Row := 0 to StringGrid1.RowCount - 1 do
begin
CellData := TCellData.Create;
CellData.StateYes := False;
CellData.Foreground := clBlack;
CellData.Background := clWhite;
StringGrid1.Objects[Col, Row] := CellData;
end;
StringGrid1.AllowOutboundEvents := False;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
Col, Row: Integer;
begin
for Col := 0 to StringGrid1.ColCount - 1 do
for Row := 0 to StringGrid1.RowCount - 1 do
StringGrid1.Objects[Col, Row].Free;
end;
procedure TForm1.StringGrid1Click(Sender: TObject);
var
Col, Row: Integer;
CellData: TCellData;
begin
Col := StringGrid1.Col;
Row := StringGrid1.Row;
if StringGrid1.Objects[Col, Row] is TCellData then
begin
CellData := TCellData(StringGrid1.Objects[Col, Row]);
if CellData.StateYes then
begin
StringGrid1.Cells[Col, Row] := '';
CellData.StateYes := False;
CellData.Foreground := RGB(255, 255, 255);
CellData.Background := RGB(255, 255, 255);
end
else
begin
StringGrid1.Cells[Col, Row] := 'Yes';
CellData.StateYes := True;
CellData.Foreground := RGB(0, 0, 0);
CellData.Background := RGB(131, 245, 44);
end;
end;
end;
procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
aState: TGridDrawState);
var
CellData: TCellData;
begin
if StringGrid1.Objects[ACol, ARow] is TCellData then
begin
CellData := TCellData(StringGrid1.Objects[ACol, ARow]);
StringGrid1.Canvas.Brush.Color := CellData.Background;
StringGrid1.Canvas.Font.Color := CellData.Foreground;
end;
end;
I'm having the next problem with SELECTNEXT and FINDNEXTCONTROL procedures in Delphi XE2 (Update 4 under WinXP Pro 32b), these procedures aren't working correctly. When a control gets focus and I try to pass to next control with the code below, it does not simply work:
procedure TformMain.cbServicioKeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13 then
SelectNext(TWinControl(Sender), True, True);
end;
I've reviewed the code for FINDNEXTCONTROL and I've created a similar procedure and I've detected the problem is in these lines:
function TWinControl.FindNextControl(CurControl: TWinControl;
GoForward, CheckTabStop, CheckParent: Boolean): TWinControl;
...........
GetTabOrderList(List);
if List.Count > 0 then
begin
StartIndex := List.IndexOf(CurControl);
...........
end;
For any weird reason, procedure GETTABORDERLIST gives a list with valid references except for the current control focused, it does that LIST.INDEXOF returns -1, and the position in LIST object for current control focused is taken for another object whose NAME property is an empty string.
I developed this code:
procedure TformMain.GoNextControl(T: TWinControl; CheckTabStop: Boolean);
var
vParent, vNextChild : TWinControl;
List : TList;
CurIndex, i : Integer;
S:String;
begin
vParent:= Self; //T.Parent;
vNextChild:= nil;
List:= TList.Create;
try
vParent.GetTabOrderList(List);
if List.Count > 0 then
begin
//CurIndex:= List.IndexOf(T);
CurIndex:= -1;
for i:= 0 to List.Count-1 do begin
S:= TWinControl(List[i]).Name;
if S = EmptyStr then
begin
CurIndex:= i;
Break;
end;
end;
...........................
Anyone has a response for this anomaly or a better solution? Thanks in advance.
d
procedure TfrmMain.ControlKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
SelectNext(ActiveControl, TRUE, TRUE);
Key := #0;
end;
end;