I'm having a problem with using a TStringGrid and Popup menu
I want to know the Row / Column of the cell that was last active when select an item from my Popup menu. However when I click on the popup menu, the StringGrid.Row is returned as -1.
I've tried using MouseToCell as part of OnClick, but even after setting SG.Row it still returns as -1 in the PopUp menus routines... I suspect that the problem is the Grid losing the focus.
Are there any solutions to this that don't require OnClick setting a global variable?
I'm using an Action List linked to the items on the Popup Menu to make sure that the actions are consistent between the toolbar and the Popup Menu
Hmm... I'm unable to duplicate the problem in my D2010.
A quick thought is that perhaps the problem occurs because you did not have any rows selected? Would presetting the StringGrid's Row to, say, 0 first in your Form's OnCreate help?
Another way to know the row of a selection on a TStringGrid (it is really the only one):
If there is only one row selected they must match.
I never see
.Selection.···
failing, while i saw YourstringGrid.Row seem to fail for getting the selection row, a lot of times it return -1 when you think it must return other values (see 4 points at the end to understand why seems to fail but it is really not a fail when it return -1, ... it is a concept bas understod).Selection and cell with foucs are not the same thing...
.Selection
is for selection,.Row
and.Col
are for the cell with the focus and has nothing related to the selection, it can be a cell with focus while selection be a total different range of cells (both are different concepts).Also more, i have detected that
YourstringGrid.Row<>YourstringGrid.Selection.Top
can be True. When the cell that has the focus is not on the top row of the selection.Some hacks, tricks, code, etc. shown on Internet are only for when
goRowSelect=False
if it is set to True such routines do not work well, use them with care.Hint: On a TStringGrid that has
goRowSelect=True
it is very buggy to select by code more than a row, changing.Selection
sometimes do not update.Row
(they do not change the actual cell that has the focus), so if anyone want to select just one row it is better to assign the row value directly to.Row
.Remember: On a TStringGrid with
goRowSelect=True
talking about what cell has the focus has no sence, so when coding it they do not have in mind such thing at all (.Row
and.Col
must not be readed whengoRowSelect=True
). In other words: if you allways have a full row selected what sence has to check the cell that has the focus, there is not such cell, it is the full row, etc... think as that to not get mad by BUGs on internal implementation when mixing focused cell on agoRowSelect=True
TStringGrid.Also worst: Whith
goRowSelect=True
and some keyboard combinations (Shift+Cursors) and mouse clicks, you can make rare selections, like two or three cells in a column, but not the full rows; remember it hasgoRowSelect=True
and the grid shows only some cells of that rows selected, and if you read.Selection
it tells you not all cells on the row are selected (True=(TheGrid.FixedCols+#<TheGrid.Selection.Left)
) where # can be more than 1. Again, beware of such BUGs... i can only say... i allways trap selection changes and force full rows to be selectect (i put code on OnSelectCell to ensure all selection is allways full row/s), see a simple code (note i do not care what cell is being selected, by concept the selection must go to a full row or full rows, not a cell; again a rare concept, internal implementation do not think you want to do something when selected cell changes, so this event is supposed to not have code, i put this code to FIX the BUG of selection not being full row/s):Simplifing: The TStringGrid is too much buggy, i have catch it telling '.Row=13' while at same time
.Selection.Top=2
and.Selection.Bottom=5
; how can be the active row one outside the selection? etc. It is because '.Row' (and also.Col
) do not talk about row selected, talk about what cell has the focus, so seeing.Row
to know what row is selected is wrong in concept... you will be seen the row of the cell that has the focus, nothing related with acutal selection cells.I never see failing this things:
goRowSelect=True
), that whole row gets selected (i had never tried that without havinggoRowSelect=True
), but only one row.Not to mention if you want to hack TStringGrid it a lot and make it a multi-row select with more than one contiguos selection at the same time (like ListBox multi select); that makes things very chazy because of all bugs TStringGrid has on .Row and .Selection properties managment.
For such multi slections grids, i recomend allways using a non oficial VCL component instead of the TStringGrid, if i do not remember bad it is called TMultiSelectStringGrid, on it you have a .Selected property for each cell, row and column that is Read/Write able. It really works great when you want a multi-select with Ctrl key pressed, also works great with multi-row selection and multi-column selectiong... just do a loop over the rows, columns or cells to check witch ones are selected and witch ones not. It also has a property
.RightMouseSelect
(if i do not remember bad) that makes right click to select, and it does well.Warning, not all code out there to select on right click is correct... a lot of them do not care if you have multi-selection or not... never set
.Row
if you want to mantain more than one row selected, just check if mouse is outside the selection prior to change the.Selection
else right click can make selection to change... so how one could popup menu for more that one row (example of use: popup menu entry called delete for more than one entry at the same time).In other words (code i really use for VCL standard TStringGrid):
Note: I really have that code on a procedure on a unit and call that procedure passing the Grid reference and X, Y coordinates; well to tell the really truth that unit i use is a full hack of TStringGrid with a declaration as
type TStringGrid=class(Grids.TStringGrid)
, so i can use visual design and have sush extra funcions on all of them; just ensure for hack to work to add such unit on theinterface
uses
section at end of the units list (or at least after Grids, never before Grids).Special hints for controlling PopUp menu been or not been shown and what Popup menu to show:
OnMouseDown
, never onOnMouseUp
, neitherOnClick
, etc.; or popup menu will be shown before selection changes... and sometines when the selection changes (by code) popup will hide inmediatly.OnMouseDown
there is no need to force the popup menu to be shown by code, it will do it normally; also more, you can cancel the popup to be shown, for example if clicked outside the cells data, or also can have different popups for FixedCols, FixedRows, also for each cell you can have a different popup (i talk about design time popups, you also can dinamically create popup entries prior to be shown, etc), put code for what you want to do allways on eventOnMouseDown
, talking about opoup menus created on design time; if popup is created on runtime, think the same: show or not show onOnMouseDown
, construction of the menu on the ownOnPopup
menu event.The basic trick is to do the selection chages on the event
OnMouseDown
, it is fired before popup menu is shown.Ah, on my code i do not mind what button was clicked, since if left clicked inside mutiple rows selected, it will also act as normal (my code do not make any change to selection, nor to row, etc. it really does nothing, see the
if
s), but you will see the selection changes to only one row.Beware, selection also can be changed to a multi selection by a mouse left down, sustained, then move mouse and lift left button, that will select more than one cell/row. All this ways that the user has to make selections, makes internal implementation of standard component so buggy, not all combinations of actions had been taken in mind while it was internally coded.
Try to press Ctrl and or Shift while left mouse is pressed and you are moving the mouse on a standard grid with no code at all, ecetp code on OnMouseMove to show
.Row
,.Col
and.Selection.···
, will see thing you will not ever think would be possible. I see one time telling.Col
value was some millons (an imposible value, since grid has only a few cols), same for.Row
(different value than when fail on '.Col').So do not believe values returned with
.Row
and.Col
if you think on getting what is the selection (wrong concept, they express what cell has the focus, nothing related with what selection is); but you can use them to select one and only one Row or Column (column only if use hacked grid that allow it, or use a grid withgoRowSelect=False
and simulate a column selection by your own).And please, allways have in mind this (please do it allways):
.Row
and.Col
may tell you which one is) can be outside the actual selection, yes, it can be out (among it is very hard to force that fail, it occurs, no need to code anything, just using mouse move click and combinations with Alt, Ctrl, Shift, Cursors and Spacebar can happen). So do not trust.Row
nor.Col
to know anything about the selection (that is wrong on concept), allways use.Selection.···
.Hope this helps not getting headage as i was having till i understand this:
.Selection.···
representes only one rectangular area of cells selected (no mind ifgoRowSelect
is True or False)..Selection:=TGrigRect(Rect(Left,Top,Right,Bottom));
is the best way to change the actual selection to another one (ensure by your selfLeft<=Right
andTop<=Bottom
or else thing can go really bad); think on code as this huge:.Selection:=TGrigRect(Rect(Min(Left,Right),Min(Top,Bottom),Max(Right,Left),Max(Bottom,Top));
(see theMin
andMax
, they are inMaths
unit)..Row
gives the row number of that special cell that has a dotted rectangular drawn on it, no matter if it is inside the selection or outside the selection, also can be -1 if no cell has focus. It has nothing related with selection..Col
gives the col number of that special cell that has a dotted rectangular drawn on it, no matter if it is inside the selection or outside the selection, also can be -1 if no cell has focus. It has nothing related with selection.Understanding that four things, i can get headages be something on the past. It took me too much to understand that two different concepts: Cell that has focus ('.Row' and '.Col') and cells selected (
.Selection.···
).P.D.: Be free to share this info!
If you want / need Right clicking a cell move focus to it (as normally done with left click) you can use the code i use for that:
I have been using that Hack
type TStringGridHacked=class(Grids.TStringGrid)
for a long, long time, since i found it on Internet.I put such hack (type declaration) just after the implementation uses (if i need it more than once on same unit), or as is in code just before the procedure (if i only need it once); both ways works fine and make code more clear.
In one of my TStringGrid-based controls I am using MouseDown/MouseUp event to handle that pop-up menu because I have two different contextual menus, depending on which area of TStringGrid you've clicked. Works like a charm. Just make sure you call inherited BEFORE your code.
--
Please note that there is something strange about the order in which the events are called when you pop-up the contextual menu. More exactly, when you press the RMB and the pop-up menu pops the MouseUp event is not called immediately. It is called next time you press a mouse button (any button).
See this also: TStringGrid - OnMouseUp is not called!
I am afraid that I do not fully understand what you mean. When I left-click a cell in a string grid, it gets selected, but not when I right-click it. When I right-click it, the popup menu is shown (if assigned), and on
MenuItemClick
I can easily read therow
andcol
currently selected. See example video.I guess that you actually want this: you want right-clicks to change the active cell as well as left-clicks. This is easily done: