Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Grid Movement 2

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
Hi all,
I noticed that the default behavior in a GRID is that a single click in a row will bring a row "highlight" (selected row) to that row. I have code then on the double-click event so that if you double click, it will select that item in the grid, and there is code to navigate to that record then.

What I'm wanting to do is have the behavior of the single-click to fire when the mouse enters the row instead. I tried putting This.Click in the MouseEnter event of the textbox in the grid column, but that seems to be ignored (I assume the GRID is somehow in control of mouse events and not the control in the column?)

Is there some way to do what I'm trying? I've tried several combinations of "This.Click()" in some events of various controls, but none seem to work.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Scott,

A quick "of the top of my head" reply:

Use MouseMove to detect when the mouse is over the grid. Then call GridHitTest to determine which item within the grid the mouse is over. I'm not sure if that will give you all the information you need, but it might be a start.

Note also that the Column object has its own MouseMove, as well as the grid.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
GridHitTest seems to be a method, and not an event. So would I call GridHitTest from within the MouseMove event, and is it the GRID's click event that changes the row? I think that's where my confusion is.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
You have grid events BeforeRowColChange and AfterRowColChange for that matter. So you can both reat to the current row being left or the new row being activated.

That by the way also happens, when users use the keyboard (tab arrow keys) to navigate the grid.

One catch is, these events also happen, when focus changes between columns of the same row, the parameters of the event only will let you detect that in AfterRowColChange, if that has the same row as the last BeforeRowColChange. Another problem is, that BeforeRowColChange only happens, when the grid haas focus and AfterRowColChange does not happen, if the focus moves to another control, so these events also happen unpaired. Sounds a bit discuraging and complicated, but the split of these events also helps and can be handles in conjunction with the When/GotFcus/LostFocus.

It's typical to put a form refresh in these grid events, though it needs a bit caution. By the way: Sometimes it helps better to SetFocus to controls instead of calling their refresh, especially for the grid itself.

The current record in the grid recordsource automatically has its active row there, no code is necessary for navigation. That's one of the major reasons code acting on the grid table/cursor is better than code acting on grid control and grid cell controls. You can be 100% sure the recordpointer is at the active row, be it visible or not.

Navigation would indeed be needed, if you intend to lready react to mouseover, then Gridhittest will give you hints on where to navigate to, how many rows forward or backward from the recordpointer recno.

Aside of that, notice the individual controls mouseovers have priority, you only detect grid.mousemove, when your mouse cursor moves along gridlines in the worst case. Binding all controls mousemoves to the grids mousemove via BINDEVENT would be a solution. It is quite incomplete despite of GridHitTest to only react to grid.mousemove, themost events happen in the columns inner text1 or whatever you added) controls, not in the grid.

In cases your grid is readonly, one simple approach is putting a transparent borderless container in grid size over the grid and use its mousemove in conjunction with GridHitTest. That's the simplest solution to track mouse movements over grids via one control and without complex BINDEVENTS usage or more complicated code designs.

Bye, Olaf.
 
Yes, GridHitTest is a method. And yes, you would call it from MouseMove. MouseMove fires repeatedly as the mouse moves over the control. GridHitTest tells you exactly where the mouse is (in relation to the grid) each time MouseMove fires.

Having said that, I'm now not completely sure if this will work for you. GridHitTest will tell you if you are over a cell. And it might be possible to find which column you are over (given that each column can fire its own MouseMove). But - if I've understood it right - you need to know which row the mouse is over. I can't see an obvious way of finding that, unless you actually click in the row.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
It might be worth using the Event Tracking tool (on the Tools menu in the Debugger) to help you figure out exactly which grid-related events fire, and in what order. That way, you can find out if any events fire for the textbox within the column, for example.

If you do, be sure to only track the very few events you are interested in, otherwise you will be overwhelmed by the large number of events that the tool will display.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Bugger, this is way more complicated then it seemed. I thought I'd just be able to access the MouseEnter event in one of the controls in the grid either column or a text box, and fire the click() event from it.

I'm still playing with it. This is one of those things you know is eventually solvable, but takes huge amounts of trial and error to get there.

I am liking Olaf's idea of putting a container over the grid, since it is read-only, and just for record navigation, this might be a clever way of resolving the problem. I'll give it a try, and see what I get out of it.
Thanks guys.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott, the underlying problem is that the grid doesn't contain separate row objects. There is really just one row, repeated many times. That might sound like a subtle distinction, but the point is that it makes it difficult to know what row you are dealing with at any one time. It is only by clicking in a cell that you overcome that (because that action also moves the record pointer in the underlying cursor, and you always know what record you are sitting on).

By the way, I wonder if you have misunderstood what happens when you call an event. You mentioned that you want to call This.Click(). Later you said you wanted to "fire the click event". Calling an event in this way does not cause it to fire. It simply executes whatever code you have in the event in question.

So if a cell's Click event caused a messagebox to display (I'm just taking this as an example), then calling its Click event from elsewhere would also display the messagebox. It wouldn't cause the cell to be clicked. In turn, that means that you wouldn't be moving the record pointer in the underlying cursor. This could be the reason you have gone wrong.

I appreciate that this explanation won't solve your problem, but I hope it will help you better understand what is going on.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike,
A subtle but important distinction...
By that logic then if I have the code DODEFAULT() in the event will it make the event happen, or just run parent code still, and not actually trigger the event? (I'm assuming that's the case based on your description). That does make a lot of sense now that you say it, and explains behaviours I have seen/not seen in the past.

So that does make this very very tricky without doing something like specific calculation of where the mouse pointer is on a fixed (or known) grid size. This grid in particular is dynamic in size, based on content. It will resize to the specific number of rows in the cursor. Sounds like this is one that I put aside for the moment and get more application function done, and then come back to it, since it works in a reasonably enough intuitive way at the moment. You just have to click to see the row highlight, and double click to select it. I was hoping to have mouse move highlight the row, and then single click select it, so it's not so "clicky"...

I had had the Click event fire the Double Click code... so it like like 1 click, but you see the row highlight for a split second before the grid vanishes on c a ThisForm.Release


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Calling events never causes them. You just cause the code you put in them to run. We have a function to raise events: RAISEVENT().

It's only the GridfHitTest done with the coordinates you get from the container overlay mousemove, that will tell you what row the mouse moves over. Because there really are not controls per Row, as Mike explained.
And instead of trying to raise the click of some row, that never exists, if it's not the active row anyway, you just navigate to the row by SKIP X or GOTO Y and that will cause the gridrow to become the active one.

DODEFAULT() calls the parentclass code, not the parentobjects, that's two totally different things, but I don't know if you confuse that at all.
By the way NODEFAULT is not the opposite, a hin on it is it's a command and not a function, if that were a pair of opposite functionalities, it would either both be function calls or commands. DODEFAULT allows you to call the parent clasbehaviour with parameters, and that can be different than your code got. NODEFAULT has no such option, not because preventing code does not need parameters, it does not act on the next parent level of a class hierarchy, it turns on or off finally triggering the base class behaviour, which is not in the realm of VFP code, but C code of the runtime implementation of classes. NODEFAULT always only plays a role in suppressing base behaviour like not reacting the a KEYPRESS and you can actually combine both and it makes sense.

Bye, Olaf.
 
Thanks Olaf, yeah you taught me about NODEFAULT before, and I have used it effectively in a few conditions in this application. It has been a savior. I get your point there.

So going back to your other point, that's an interesting notion as well, to instead of act on a click, get the record pointer to move. I think I might be able to do that by looking at the GridHitTest now that I understand it as a method better. So I'm thinking, my grid header is 15pixels high, and my grid rows are 19 pixels high, so if I do a GridHitTest and get the nXCoord_In value, (I assume this is position relative to the object, and not to the form?) then I could calculate where it is on the gird, if it goes above the next 19 pixles+15pixles, then it has increased the row, if it is less than the previous value and within the top of the grid and the next row, then I can issue a SKIP or SKIP -1 depending on the direction that is moves, and if I understand correctly, that will change the highlight of the value?
Is this in the ball park? I'll give is a try.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Ok, I clearly am not used to this... This is kind of confusing, can you help explain this to me, as the Help File doesn't really provide any examples to help me grasp how to use the GridHitTest.


So Help shows: Grid.GridHitTest(nXCoord_In, nYCoord_In

[, nWhere_Out [, nRelRow_Out [, nRelCol_Out [, nView_Out]]]])

So am I supposed to pass the values of nXCoord_In and nYCoord_IN? And how are these "Out" parameters? In the properties it says "Returns location information in the grid for the specified x- and y- coordinate". So the presence of the "output" parameters on this same line in the explanation of the method doesn't make sense to me. How would I issue a call to this method in a way that is useful in the MouseMove event?
Which would tell me when I passed a cell?

Then I could skip up or down based on the prior cell reviewed (or stay put if I'm still in the same sell)
I had envisioned something like this in the MouseMove event:

lnCurrentWhere = lnWhere
lnWhere = This.GridHitTest(MCOL(),MROW(),3)

CASE lnWhere > lnCurrentWhere
SKIP
CASE lnWhere < lnCurrentWhere
SKIP - 1
ENDCASE

But I get an invalid function argument, type or count message back.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Out parameters are parameteres you pass in by refer3ence via @ in front:

Code:
nWhere=0
nRelRow=0
nRelCol=0
nView=0
GridHitTest(x,y,@nWhere,@nRelRow, @nRelCol, @nView)

Also, in the continaer.mousemove you get parameters LPARAMETERS nButton, nShift, nXCoord, nYCoord
And instead of MROW() and MCOL() you just forward nXCoord, nYCoord, especially if your container has same size and position as the grid no translation is necessary.

Bye, Olaf.
 
I'm sorry if this is a stupid question, but this still feels foreign to me.

So if I call from MouseMove event in the GRID:

GridHitTest(x,y,@nWhere,@nRelRow,@nRelCol,@nView)

With expectation of the values set as Olaf describes, what will I "get back"? How can I use that data? I also don't understand why the @ values have to precede some of those values but not others? And doesn't x and y have to get set some how? Sorry, very lost here.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott, this can indeed be confusing - but stay with it.

An "output parameter" is a parameter that you pass to a function (or method), just like any other parameter but with one important difference: the function can change its value; and, when it does, that new value is visible to the caller.

The parameter must be a variable (or a custom property), not a literal value. The variable must already exist in the caller. When you pass it to the function (or, in this case, to the method), you precede it with @.

Olaf has given you an example of how you do that. What you are missing is that the method (GridHitTest) places its results in the output parameters. So, after calling the method, the "where" value will be in nWhere, the relative row will be in nRelRow, etc.

Where you are going wrong in your code is to pass a literal value (3) as the third parameter. It needs to be a variable, and it needs to be preceded by @.

You are also wrongly assuming that GridHitTest actually returns the "where" value in the normal way. It doesn't. It returns a logical value: .T. if the mouse is in the grid, .F. otherwise. That would give you a data type mismatch error (if you had got that far).

I hope this helps, and hasn't just confused you further.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
When I said earlier that you cannot determine which row the mouse is over, I hadn't taken in the GridHitTest can return the relative row number, that is, the row number relative to the top-most visible row in the grid. You should be able to combine that with the RelativeRow and ActiveRow properties to determine which actual row of the grid you are over.

I won't try to give an example as it is late at night here now and I would probably get it wrong anyway, but you might want to experiment a bit with this.

Once you know which absolute row you are on, you will also know the record number in the underlying cursor, and you can use DynamicXXX properties to highlight the row in some way.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Yes, of course you have to set some x and x, I didn't specify that, as it depends from where you want to call this. I already said you can use the nXCoord, nYCoord for that.

And what do you do with the return values?
Well, read the help, what these values are is described there.

You know best what you want to do, most probably you want to color the row under the mouse position, or activate that row. That only makes sense, if nWhere=3 and you hover a cell, not a header, gridlines or other grid components.

RelativeRow/Col are relative to the active Row/Col top row visible in the grid. As the help says you may use these as parameters for the ActivateCell method.

Put Thisform.Container1 over Thisform.Grid1 at exaclty same position and set container1 backstyle transparent then in Thisform.Container1.MouseMove:

Code:
LPARAMETERS nButton, nShift, nXCoord, nYCoord

nWhere=0
nRelRow=0
nRelCol=0
nView=0
IF Thisform.grid1.GridHitTest(nXCoord, nYCoord,@nWhere,@nRelRow, @nRelCol, @nView) AND nWhere = 3
   Thisform.grid1.ActivateCell(nRelRow, nRelCol)
Endif

Now hovering the mouse activates a cell.

Bye, Olaf.
 
Olaf,
This has to be the most amazing thing you've taught me. I have to admit, I don't fully understand at this point why it works, but it is exactly the behavior I have been trying to generate! I then only needed to add in the container click event to execute the GRID's click event code (which selects the record, and moves the parent table to the right location).

This is some serious voo-doo.
I'm going to take some time to look at this in the debugger, so I can better understand what's actually going on. The @ operator is both interesting and confusing. This is really really cool. I can't thank you enough.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
>I then only needed to add in the container click event to execute the GRID's click event code (which selects the record, and moves the parent table to the right location).
No, because ActivateCell already did that, the text cursor is within the activated cell, thus the record is active, too.

Well, you might not see this, when the grid is readonly, I haven't tried that.

What you don't have is the click inside the tetbox to select the text or position the text cursor, but as you said the grid is readonly that would only be necessary to select text.

If you go that route, I don't know why make it so complicated in the first place? If you want the user to click on a cell to activate it, that is the default behaviour of the grid. Maybe you lack the understanding of some properties as AllowCellSelection, Readonly and Enabled? If you want to allow cell activation readonly, you only set Readonly=.t., keep others at default. Then users can activate cells and select text or values in general. There is no voodoo needed for that.

I see the biggest disadvantage of that hovering activation in the user loosing control when to end that hottracking, eg you mouse over your destination cell, then change from mouse to keyboard and slip and activate another cell. The normal interface to activate with a click - what you also aim for - needs no extra hottracking. It might be a nice visual indicator, but doesn't give any real help in my understanding of UX. Your idea of needing a click at the end also confirms you yourself think this way about the usual interface, you click on something you want to activate. Automatic activation during only hovering can cause all kinds of negative side effect of changing the buffer status of any field bound to any cell you hovered over, just to mention one thing. You also might hang on any vlid event of grid controls, just because you hovered over them.

Bye, Olaf.
 
Hi Olaf,
Thanks for the warnings there. I think if you could see it it might make more sense. Mike suggested alternatively to use a dynamic menu, but I found that a bit complex (yeah, 'cause this was so easy! NOT). But I'd gone down this path so far, that it seemed like it would be starting all over again to do it as a menu. I got the initial part of this idea from something you mentioned probably 2 years ago now, about using PEMSTATUS to look at (I think you suggested radio button at the time) to turn points on or off on a "map". I used a flag image (turn it purple or green, depending on which is active where there is more than one flag). So that was the initial implementation. Then I realized, some sites had more than one objected associated with them, so I set the tootiptext to display the location name (the flag didn't really show that, and the map it was on is a world map, so 1 pixel is equal to about 70 square miles), and that worked nice.
Then I got to thinking, wouldn't it be more useful if I could get a list of the sites, and then pick it from there. I tried a few things that didn't work, and then Mike got me pointed in the right direction. The idea was to NOT have to click. That worked for the most part, but the grid would highlight the top row and no matter what I tried, I couldn't turn it off. So then I thought, well, if I can move that highlight row by hovering over the item in the list, that would be ok. I want the user to make a conscious choice to move to that record though, as opposed to just examining its contents. Hence, the behaviour I've tried to get out of it.
I do see your point though about user interaction away from the mouse. I may be underestimating user behavior, but they don't have a way to bring that list up without pointing the mouse over it. So I thought keyboard navigation through that list afterward is unlikely (just like when you click on a top line menu, you don't then switch to the keyboard to navigate through the list items, you click on them). If you navigated through it by keys, then it would be because you "hot keyed" that menu open. I don't give that option in this case, but if I did, it should still work by keyboard, I would think? It terminates when the mouse leaves it, so another reason I didn't put keyboard opening on it. I suppose I could set ESCAPE as a means of releasing the form as well.
I've redacted an example that's attached here as well. You'll note I also provide a grid navigation at the right, this is just for convenience when you're looking at the flag info by point the mouse at it. The popup next to the flag just shows the records for that flag, where the navigation at right, allows for traversing through all the objects associated with that company.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
 http://files.engineering.com/getfile.aspx?folder=d1dd08dc-4e74-4910-a17f-ca676fb34256&file=DCideFacilities.jpg
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top