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

Container Object inside a Grid 2

Status
Not open for further replies.

Alec_Gagne

Programmer
Apr 14, 2022
15
US
Greetings all you smart VFP people...

Maybe I am trying to do something that can't be done, but that is usually not the case with VFP!

I have a container object ('MyDispContainer') with other objects (shapes) inside that has been designed to display graphic representations of data. Similar to, but not exactly like, a slider control.
I want to embed this container in a grid column and have it update based on the data value of the record displayed in each row of the grid.
The issue, as you probably already know, is that a container object does not have a ControlSource property that can be used to bind to a field in the underlying table.

I have to set SPARSE=.F. in the grid column so that container is displayed, even though the default TextBox control for that grid column has been deleted/removed and the grid column CurrentControl='MyDispContainer'. If SPARSE=.T. the container simply does not display.

Since I cannot bind to the row data, I tried inserting some code into BeforeRowColChange method of the grid to simply update the container with the current row data but since SPARSE=.F. all the Container objects on each row of the grid update with the same values and that is not what I need.

Does anyone have any thoughts, strategies or clever trick for getting my container control to display the properly based on the underlying row data when the container has no ControlSource?

Thanks

A.J.
 
I have used containers in the only grid column of a grif to let them display a record as I like, with multiple lines, labels, etc. I don't remember whether I added something like the value property, but just controls that have a controlsource. Even if only that works, you surely can have a textbox at coordinates outside the visible container area, ie with top=-100, to have the bound value and use that to control the shapes in the visible container area.

I'll have a look and be back with an example.

Chriss
 
Welcome to the forum, A.J.

I have done this sort of thing in several of my applications. I have a grid class in my framework: the grid has a single column, and this contains the container. I find it is very effective for diplaying data, but - for the reasons you have discovered - it cannot support direct editing of the data by the user.

Instead, I provide a different mechanism for editing the data, and then regenerate the grid after the user has done that. Typically, I let the user double-click on a grid cell, at which point a modal dialogue opens showing editable controls (textboxes, etc) for the relevant record. When the user OK's out of the dialogue, I update the underlying cursor and refresh the grid.

Since the grid is in effect read-only, the container does not contain textboxes (or other editable controls). Instead, it contains labels and images, which can be arranged in a variety of ways.

It was quite a lot of work to create the grid class in the first place, but having done that, using the class is very easy, and lends itself to an attractive way of displaying data.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Do you know how an imagecontrol works in a grid? By using an Access method for Backstyle? BackStyle_access()?

The same works for a container.

Create a class named - for example - Columncontainer and add an access method Backstyle_access

In that method you could do things like Evaluate(This.PArent.Controlsource) to evaluate the value of the column.Controlsource, or simply access the current record of the grid recordsource directly, any field you like. That backstyle_access runs whenever the grid also draws anything else in a row with the recordpointer on the record of that grid row.

Just don't forget to end the BackStyle_Access method of the Coloumncontainer class with RETURN This.Backstyle.

Here's an example drawing hour/minute hands of a time stored in a datetime field:
Code:
LOCAL Appointments

Appointments = CREATEOBJECT("Appointmentsform")
Appointments.Show()
ON KEY LABEL ALT+F4 CLEAR EVENTS
READ EVENTS
 
DEFINE CLASS Appointmentsform AS form
	Height = 380
	Width = 360
	Caption = "Appointments"
	Name = "Form1"
	DoCreate = .T.

	ADD OBJECT Grid1 AS grid WITH ;
		ColumnCount = 2, ;
		DeleteMark = .F., ;
		Height = 354, ;
		Left = 12, ;
		RecordSource = "Appointments", ;
		RowHeight = 111, ;
		Top = 12, ;
		Width = 337, ;
		Scrollbars = 2, ;
		Name = "Grid1", ;
		Column1.ControlSource = "Appointments.AppointmentWith", ;
		Column1.Width = 149, ;
		Column1.Name = "Column1", ;
		Column2.ControlSource = "Appointments.ScheduledFor", ;
		Column2.Width = 156, ;
		Column2.Name = "Column2"

	PROCEDURE Load
		CREATE CURSOR Appointments(Id integer autoinc, AppointmentWith V(50), ScheduledFor T)

		INSERT INTO Appointments (AppointmentWith, ScheduledFor) VALUES ("The Joker" , {^2022-04-01 09:30:00})
		INSERT INTO Appointments (AppointmentWith, ScheduledFor) VALUES ("The Easter Bunny" , {^2022-04-17 15:00:00})
		INSERT INTO Appointments (AppointmentWith, ScheduledFor) VALUES ("Climate Change" , {^2050-12-31 11:55:00})
		GO TOP 
	ENDPROC
	
	PROCEDURE Init()
	    WITH Thisform
	       .Grid1.Column2.AddObject('Timecontainer1','Timecontainer')
	       .Grid1.Column2.Timecontainer1.Visible = .t.
	       .Grid1.Column2.CurrentControl = 'Timecontainer1'
	       .Grid1.Column2.Sparse = .f.
	    ENDWITH 
	ENDPROC
ENDDEFINE

DEFINE CLASS Timecontainer as Container
   BackStyle=0
   ADD OBJECT Label1 as Label WITH Backstyle=0
   ADD OBJECT HourHand as Line
   ADD OBJECT MinuteHand as Line
   
   PROCEDURE Backstyle_access()
       LOCAL DateTimeValue, TheoreticalWidth, TheoreticalHeight, Radians
       DateTimeValue = EVALUATE(This.Parent.Controlsource)
       
       This.Label1.Caption = DTOC(DateTimeValue)         
            
       WITH This.Hourhand
            .Top=This.Parent.Parent.Rowheight/2
            .Left=This.Parent.Width/2
            .LineSlant='\'
            Radians = PI()/2-PI()* ((Hour(DateTimeValue)%12)/6+MINUTE(DateTimeValue)/360)
            TheorecticalWidth = 40*COS(Radians)
            TheorecticalHeight = -40*SIN(Radians)
            
            IF TheorecticalWidth <0
               .Left = .Left + TheorecticalWidth
               .Width = -TheorecticalWidth 
               .LineSlant= IIF(.LineSlant='/','\','/')
            ELSE
               .Width = TheorecticalWidth
            ENDIF
            
            IF TheorecticalHeight <0
               .Top = .Top + TheorecticalHeight
               .Height = -TheorecticalHeight
               .LineSlant = IIF(.LineSlant='/','\','/')
            ELSE
               .Height = TheorecticalHeight
            Endif
       ENDWITH

       WITH This.MinuteHand
            .Top=This.Parent.Parent.Rowheight/2
            .Left=This.Parent.Width/2
            .LineSlant='\'
            Radians = PI()/2-PI()*Minute(DateTimeValue)/30
            TheorecticalWidth = 50*COS(Radians)
            TheorecticalHeight = -50*SIN(Radians)
            
            IF TheorecticalWidth <0
               .Left = .Left + TheorecticalWidth
               .Width = -TheorecticalWidth 
               .LineSlant= IIF(.LineSlant='/','\','/')
            ELSE
               .Width = TheorecticalWidth
            ENDIF
            
            IF TheorecticalHeight <0
               .Top = .Top + TheorecticalHeight
               .Height = -TheorecticalHeight
               .LineSlant= IIF(.LineSlant='/','\','/')
            ELSE
               .Height = TheorecticalHeight
            Endif
       ENDWITH
       
       RETURN This.BackStyle
   ENDPROC 
ENDDEFINE

Which should show as
appintments_oweelj.png


Besides, unlike Mike I also found it easy to have controls in a container with controlsource set to whatever fields to edit that record. But in your case the container isn't for the full record anyway and just for readonly display with shapes, so that won't matter. My Example uses Line Shapes, so it's very close to what you want.

Chriss
 
The important part is using a backstyle_access method and making the column Sparse=.f., the rest is just an example to show it works.
And to have a backstyle_access method you have to create a class.

As nice as posting PRG code for a form is for sake of copying, if you design the form visually I'd also create a visual class for the container, be it for the simplicity of dragging it onto the form into the column of the grid.

Chriss
 
I've used the Backstyle access method to give the grid a striped effect, with alternate rows having a tinted background. But, A.J., given that you are just starting work on this kind of grid, I'd suggest that you don't get distracted by that. Get the basics working first, then look to build on it.

I note what Chris says about controls in the container with a ControlSource (in other words, editable controls such as textboxes). I haven't been able to get that working in a satisfactory way, but don't be put off by that. It's probably down to my ignorance.

That said, I've always tended to avoid using any kind of grid for entering or editing data. That's just a personal preference. Others may not agree.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike Lewis,

there might be some details for getting it to work, but taken for granted it can be done, there's also still limited use of such containers when they would become too large with too many fields to edit and the container becomes sort of a subform. This actually was the origin of that construct, to emulate MS Access subforms with such containers.

If the rowheight has to become very high, the grid loses its purpose of a list overview and perhaps also picker control with more features than just a listbox.

A.J.,
as I understand, you just want to draw something like a slider, I assume a horizontal slider, so it won't need a high rowheight. That's totally doable with this container.backstyle_access method.

Chriss
 
Chris & Mike,

THANKS!! All great feedback here. It is always good to hear that others have blazed the trail before you......

Yes, I am very familiar with _access() and _assign() methods of object properties so this implementation was easy to add to my container class. My grid is also strictly for display purposes only so I'm not worried about user edits here.
I was looking for an event that would fire when the underlying data changed but had not found one.

This approach worked like a charm. Exactly what I was after!!!

THANKS AGAIN!

A.J.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top