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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

HOVERING GRIDS WITH HOVERCONTAINER CLASS 2

Status
Not open for further replies.

Gerrit Broekhuis

Programmer
Aug 16, 2004
316
NL
Hi,

Recently I have worked on a class that will add hovering to a grid. In other words, the cursor will follow the mouse movements on a grid. This helps to create a responsive and interactive interface. I added some extra features like AutoScrolling, MultiSpeedScrolling and Incremental Search. I have created a zip file with the class and a sample form to show how it works.

I have made a webpage ( with a lot of extra information, the download and a video. The video is on YouTube too (
In the documentation and on the webpage you will find the following topics:

- Introduction
- How about hovering in Visual FoxPro?
- Development
- Incremental Search
- AutoScrolling
- MultiSpeedScrolling
- Ho Ho Ho!
- How does it work?
- Form enhancements
- Sample form
- Release notes

Creating this class would not have been possible without the online help I received from several people. Thank you all once again!
Any improvements, additions or bugs can posted on this forum or you can use the contact information from the webpage.

Regards, Gerrit

<i>Today is only yesterday's tommorrow - Uriah Heep</i>

thumbnail_small_dlooie.png
 
Very nice Gerrit, and thanks for sharing !

Edgar

Edgar
Integrated Bar Code Systems, Inc.
 
Thanks for sharing, Gerrit.

Just one question: Why are you using object names instead of just using object references? You could even strike off a requirement you make to your users to use the NAME clause of the DO FORM, if you just use this:
Code:
ON KEY LABEL ENTER do KeyboardEnter with _screen.ActiveForm
and then
Code:
PROCEDURE KeyboardEnter
LPARAMETERS toForm
Then using toForm instead of &cForm., so for example

Code:
lcGridName = toForm.hovercontainer1.GridName
Which in itself could also use a grid control object reference instad of just the grid name.

You're better off with references than names anyway, even in case you'd need to know the name of an object that's simply objref.name, but usually there's no need to know names if you have references.

Chriss
 
Making the change in the hovercontainer.init:

Code:
LOCAL loGrid, liGrid, lnGridRowHeight
FOR m.iGrid = 1 To Thisform.ControlCount
	oControl = Thisform.Controls(m.iGrid )
	IF oControl.BaseClass="Grid"
		lnGridRowHeight = oControl.RowHeight
           This.GridHovered = oControl
           Exit
	ENDIF
ENDFOR

Well, besides this would just take the first grid on the form, you don't seem to think of more complex forms with maybe nested pageframes each having a grid using a hovercontainer in the future.

One way a hovercontainer could automatically know "its" grid is by usage of gridhittest with the coordinates of itself.

Code:
LOCAL loGrid, liGrid, lnGridRowHeight
FOR m.iGrid = 1 To Thisform.ControlCount
	oControl = Thisform.Controls(m.iGrid )
	IF oControl.BaseClass=="Grid" and oControl.Parent = This.PArent and oControl.GridHitTest(This.Left+This.Width/2,This.Top+This.Height/2)
           lnGridRowHeight = oControl.RowHeight
           This.GridHovered = oControl
           Exit 
	ENDIF
ENDFOR

Which means the hovercontainer will care for the grid under itself, on the same parent control, which may be a page or a container or the form. Anyway, this also makes it very mandtory to not just refer to object with names, as in more complex forms these will not just be directly on the form, so names are multipart names anyway, like thisform.pageframe2.page4.container2.companygrid and your simple name feature will fail to find it on thisform.&lcGridname.

Chriss
 
Looking back, loGrid isn't even used, that could replace oControl, minor issue.

More important, I forgot thisform.controls also is only having controls that are directly on the form, any control on a pageframe needs to be found by deep diving into the pages of pageframes, for example.

This is what I came up with in tests. Done that way a container finds the grid at its own coordinates (I do a gridhittest at the center point of the hovercontainer), so this would be the appropriate part of the hovercontainer.init setting its Hovergrid object reference property

Code:
Local loContainer, liControlNo, loControl, lcControls, llExit, loCollection As Collection
loCollection = Createobject("collection")
loCollection.Add(This.Parent) && was thisform, earlier.

For Each loContainer In m.loCollection FoxObject
   Do Case
      Case Pemstatus(m.loContainer,"Controls",5)
         lcControls = "Controls"
         lnCount = m.loContainer.ControlCount
      Case Pemstatus(m.loContainer,"Pages",5)
         lcControls = "Pages"
         lnCount = m.loContainer.PageCount
   Endcase

   For liControlNo = 1 To m.lnCount
      loControl = m.loContainer.&lcControls[m.liControlNo]
      If Pemstatus(m.loControl,"Controls",5) Or Pemstatus(m.loControl,"Pages",5)
         * process inner controls of forms, containers, and pageframes in further passes
         loCollection.Add(m.loControl)
      Endif
      If m.loControl.BaseClass=="Grid" And m.loControl.Parent = This.Parent And m.loControl.GridHitTest(This.Left+This.Width/2,This.Top+This.Height/2)
         This.oControl = m.loControl
         llExit = .T.
         Exit
      Endif
   EndFor
   
   If llExit
      Exit
   Endif
Endfor

I avoided to use a recursive method by processing a collection object, which at the start only contains the form object (thisform -> better this.parent, no need to start at the root if you know you want to find objects on the same nesting level as the container), but if a control of it is a pageframe or container, which has a pages or controls collection, that's added to this collection and processed in further iterations, so finally all controls of the form are looked at until the hovercontainer finds its grid, that is identified by being at its position and having the same parent object, ie a hovercontainer over a grid in page1 must also be put into page1 on top of the grid, not just on the form level, just visually at the same position, but having the form as parent and not page1. That also avoids a hovoercontainer to potentially cover two grids on page1 and page2 visually at the same position, but not visible all the time. If you see what I mean.

Not that important. For your current use case it would still be sufficiently finding "its" grid. Just all code using the gridname needs to be refactored to use the grid object reference instead. It won't help, if you modify my code and set This.GridName = loControl.Name, because the grids name relative to the form isn't just thisform.&gridname, it can be somethinng like thisform.pageframe1.page1.grid1, and the easiest way to address it correctly is by the control referrence, that is loControl itself. So siply store that into hovercontainer.hovergrid, which is a property replacing the gridname property.

Edit: Important last code modification. The previous version was used in some debugging session.

Chriss
 
I haven't looked into everything you do, Gerrit. Overall it's still a lot you offer there, for all. Thanks. I also second the idea, since the grid itself is not simple to handle with mouseevents alone, there'll fire mouse events of the grid only when your mouse pointer is exactly over a gridline, then mouse events of the inner textboxes and so on, it's much easier to handle this aspect of hovering and also the scrolling feature you added by mouse events of a container that is put in front of the grid. Even though you could also use bindevents to delegate handling of all these different events in one central handler. It's not simple to orchestrate an ant farm of objects.

So the overall idea is nice. I used it already too. One thing I did in my container is a selection of multiple cells drawing a container with a dotted line border within my "hovercontainer" and using gridhittest to collect all the detail objects within the area of that selection container. Just an idea.

And to put a container in front of something else to handle all mouse events of that rectangle conveniently can also be used in many more cases with other things underneath it.

Chriss
 
Well, and as comfortable as it is to automatically let the hovercontainer find its own grid, the simplest would be a property set manually to thisform.pageframe1.page2.grid1, if that is the grid. The container should just ensure it really is at the position of the grid, I think you already take into account resizing, so that's fine, the container should cover the whole grid area, of course, and that's worth automating, it also takes less code to do, once you know "your" grid.

Chriss
 
Hi Chris,

First of all big thanks for looking into this project. I very much appreciate your very detailed comments and I’m sure I will be able to make these (and perhaps other) improvements.

I will collect all comments from you and others and of course I will work on an updated version later.

Regards, Gerrit
 
You're welcome. I may look more into the rest of the code, I downloaded it. I just consider it'd be worth feedback, as it makes the usage of your container simpler.

I experimented a bit, you could also get all mouse events, if you make a grid based on the control class. But when it comes to replacing existing grids with a new class, it's simpler to put a container in front of a grid to get new behavior.

I thought about this on a more general level of how could something general be added to any control by developing a separate class that always can be added to have that behaviour. And regarding mouse events a container in front of anything is perhaps the most elegant, even though BINDEVENT also has some merits, it is designed to have one extra handler class anyway.


I played a bit with SetAll, even though I'd rather like to have a GetAll, a SetAll used with a property that has an assign method can be used to return something, if you feed in an object the assign method then doesn't just set as a property value, but uses to set a return value, aka something like a by reference parameter. In the simplest case when you use Thisform.SetAll('somepropert',oCollection) where oCollection is a collection, every assign method could add objectss to that, for example simply "THIS", each control can add itself to the collection and then you have all controls of a form with one SetAll() call.

But it requires more than just a common property, which we have a few of, like name or tag or comment. It requires the assign method code and that means subclassing all base controls, which commonly is not what projects have. It would slim down the init routine for a hovercontainer to find its grid, for example, but can be used for many more ideas. It just fails at this implementation hurdle.

What would actually help is a mechanism of superclassing rather than subclassing.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top