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!

Image in a Grid 1

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,795
10
38
JP
Hi All,
I've been reading through some old posts for a solution, but it seems everyone approaches this a little differently.
So I have a table of documents, and I wanted to be a bit "clever" and show the icon associated with the file type. (Word, Excel, PDF, etc). The types are defined in another table that has the file type, extension and icon image associated with it.

So this means each document has a memo field which holds the path to the image type for the "icon" (We made them ourselves, not trying to retrieve them dynamically thought that would be really cool).

So I set up a 3 column Grid: Filename Extension Icon

In column 3 I removed the text box, and put in an image object instead. However, in the Image properties I tried to set Document.Icon (a memo field in the table that has a fully qualified path to the image location, such as T:\MyApp\Configs\FileIcons\Excel2007.PNG

I had expected that as the records were added to the row, the image class would show that .PNG automatically, just like when I point it's picture clause to This.Picture = Document.Icon. While that works at the form level, it doesn't work in a grid... how do I get this to show these by reference to the memo field? Most of the "solutions" I saw were hard coding image types into a field and using dynamiccontrol, but that seems to just pick between differently defined image objects in the column, and I have far to many file type icons for that... Plus I want the addition of new icons to be driven by our "FileType" table, instead of having to code them.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Yes, the problem is the image control has no controlsource, neither the Picture nor the new in VFP9 PIctureVal will help you to set such a construct that automatically updates. This is like a textbox would be without controlsource, only the Value property. Whatever you put there is just the value, there is no activity evaluating that value on a current record.

Therefore you have to use some trickery, knowing how VFP draws the grid. It reads the backstyle property of the control. So creating an image class with a backstsle_access method gives you the point in time, where you might set Picture to the path in your data.

So let me search for you, this is described in thread184-1738143 in where I Define Class myImage as Image and have demo code using it.

Bye, Olaf.
 
Hi Olaf,
Ok, I'm reading your thread now, so am I correct that if I take my current image-subclass (imagebase) and add the method Backstlye_Access to it, I can use it as you describe with your custom class?

In the Method, add the code:

This.Picture = Evaluate(this.ImageFileName)
Return This.BackStyle

And add the property ImageFielName
To the image class as well?

So I'll try it now.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Well, yes, mostly.

Instead of This,ImageFilename you might use your Document.Icon, the Document alias will be accessible within the code and the grid is at the record you want to display.

Also look closer, I used Evaluate(this.ImageFileName) for a reason. If you add such a property, this becomes a property quite like the controlsource (only one way, though, not writing back anywhere). If you then set .ImageFileName = "Document.Icon", then Evaluate(this.ImageFileName) will mean Evaluate("Document.Icon"), which is nothing else but Document.Icon itself. When you only use that for this one case, you can also go more directly and have Document.Icon in your code instead of going through this hoop.

Bye, Olaf.
 
Hi Olaf,
Making some progress, but not familiar with some of the more advanced issues.
So I have modified my imagebase class to have the Backstyle_Access() Method, and in that method I have put in

This.Picture = Evaluate(this.ImageFileName)
Return This.BackStyle

As Above. And I've added the property ImageFileName to the imagebase class as well.

But I'm tripped up on the INIT, which I believe goes into the form level init?

So I put this code into the INIT of the form:

WITH ThisForm.grdCodeField.Column3
.AddObject("cont1","ImageBase")
.cont1.IconImage.picture = 'ImageFileName'
.cont1.resize
.cont1.visible=.T.
.Sparse=.F.
ENDWITH

I think at least part of my problem here is related to the object reference? I've not used the "WITH" before, so not sure if I'm referencing it right. I had the oGrid reference first, and that gave me an undefined object, so I changed it to the reference in the form ThisForm.grdCodeField which is the name of the grid in my form. I'm also mixing between your "code" level definition, and trying to use my class library, and translating between code and "Form" feels a little awkward. Bare with me, this is new learning territory for me. How do I correct this point?

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
.cont1.IconImage.picture = 'ImageFileName'

You won't set that, the code of BackStyle Access sets Picture, nothing else must do so. You have to set ImageFileName to "Document.Icon" for it to work.

Let's face it, if you had a textbox you also don't set Textbox.Value = "Controlsource", you set Textbox.Controlsource = "table.field". The Controlsource evalates its own expression and puts that in Value. Just like the Backstyle_Access() code does and evaluates ImageFilename and puts it in Picture. So it's never Picture fetching what you have in ImageFileName but its the other way around. The event of accessing the Backstyle property (which compares to refresh or record navigation in regard of the grid acessing it) is making the code put the Picture value in. Picture is just an inactive proeprty, it never is active and you also can't make it active by adding access or assing methods. The solution makes use of the grid accessing the backstyle property of an image control to check, whether the picture needs to be drawn with tansparency or opaque. The Picture itself should also be accessed, so the grid knows what to draw, but it may not access it more than once. Anyway, creating a Picture_access method will not be be called by the grid for every row. If that would be the case, you could also let Picture_access return Document.Icon directly. But that's not how it works.

So it all relies on well known grid behaviour.

Besides all this, it won't work, if your image control is in a container, then your grid.column directly contains a container containing an image control, that's never working.

Bye, Olaf.
 
Olaf,
I'm confused... I don't have the control in a container. I'm just following the code from your thread, but I'm struggling with understanding how it works. The "Well known grid behavior" you mention, I'm quite certain is well known to you, but completely mystery to me.

My ThisForm.Init breaks immediately when I try to run this. I'm assuming because, where you've created the form programmatically, so it all works fine "in code", I'm creating a form using the form designer, and I'm trying to use the base classes that I have already, but where your's defines these as "simple example" I am not clear on how to translate the "in-code" of this to a Form, with my base classes on it.
During the Init I get the message "Class definition IconImage is not defined".

The code is
Code:
DODEFAULT()

WITH ThisForm.grdCodeField.Column3
	.AddObject("cont1","Imagebase")	
	.cont1.Imagebase.picture = 'File Icon'
	.cont1.resize
	.cont1.visible=.T.
	.Sparse=.F.
ENDWITH

So Imagebase (I thought) would be a reference to my image class in the baseclass library. Do I need to do something else to tell VFP that it's that class I want to create the new object in Column3 on?

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

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

.AddObject("cont1","Imagebase")
.cont1.Imagebase.picture = 'File Icon'

That just reveals if not proves, your real image control is not ImageBase, but ImageBase is a container, having an Imagebase image control, which in turn has the picture property.

So just open up "your" IamgeBase and see for yourself. What looks as one control in the designer must be an image control in a container, just looking as if it was a plain simple image control, because this has same size, perhaps.

You can't fool anybody looking precisely. You have grdCodeField.Column3.cont1.Imagebase.picture and if ImageBase would merely be based on picture, you wouldn't have that layer.

Bye, Olaf.
 
Ah, ok I see part of the issue here now. I was reading that thread you mentioned above, and I picked up the wrong code from it...
So now my INIT is this:
Code:
WITH ThisForm.grdCodeField.Column3
	.AddObject("Imagebase1","Imagebase")
    .CurrentControl = 'Imagebase1'
    .Width = 24
    .Sparse = .F.
    .Imagebase1.ImageFilename = "Imagebase1.FileName"
    .Imagebase1.Visible = .T.
ENDWITH

Where Imagebase is meant to be from my baseclasses library. But it doesn't seem to recognize my class library? Or is there something I need to do to tell it to use the image definition in that library?

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
There only is one post where I define Class myImage as Image

Code:
Public oForm
oForm = Createobject('myForm')
oForm.Show()

Define Class myForm As Form
  Height = 400
  Width = 800
  Add Object myGrid As Grid With Height = 400,Width = 800,RecordSource = 'myImageList'

  Procedure Load
    Local Array aImageList[1]
    Local lcPath
    lcPath = Home()+'Graphics\Bitmaps\Offctlbr\Large\Color'
    Create Cursor myImageList (FileName m)
    For ix=1 To Adir(aImageList, Addbs(m.lcPath)+'*.*')
      Insert Into myImageList Values (Addbs(m.lcPath)++aImageList[ix,1])
    Endfor
    Go Top
  Endproc

  Procedure Init
    With This.myGrid
      .ColumnCount = 2
      .RowHeight = 24
      With .Columns(1)
        .AddObject('EditBox1','EditBox')
        .CurrentControl = 'EditBox1'
        .Width = 720
        .Sparse = .F.
        .EditBox1.Visible = .T.
      Endwith
      With .Columns(2)
        .AddObject('myImage1','myImage')
        .CurrentControl = 'myImage1'
        .Width = 24
        .Sparse = .F.
        .myImage1.ImageFilename = "myImageList.FileName"
        .myImage1.Visible = .T.
      Endwith
    Endwith
  Endproc
EndDefine

Define Class myImage as Image
   ImageFileName = ""
   Procedure Backstyle_Access()
      This.Picture = Evaluate(this.ImageFileName)
      Return This.BackStyle
   Endproc
Enddefine

Notice myImageList is a Cursor here, not an object name. It still works as is.
You need a new Imageclass, it seems, as your's is having the surrounding container.

Bye, Olaf.
 
Hi Olaf,
No, there is no container on my imagebase class. 1) I checked it in the class browser, and 2) the "confusion" in the code came from the thread that you posted the above code in, from another user, but I got confused thinking his code was from a form, and not realizing he was using a container in his example. That was my fault.

So I'm looking what you have above, and I have put the LOAD code into my form's LOAD event, so it also creates the list of images (from my "CONFIG\FILEICONS" directory.
I found another mistake in my INIT procedure, where I changed "myImageList" to "Imagebase1.Filename" so that was wrong, as that would be the control name not the cursor.

I watched it in Debug, and the cursor populates perfectly, but when I get to the INIT line with .AddObject('Imagebase1','Imagebase') it fails with the error "Class definition IMAGEBASE is not found."
I know I have this class in the class library "baseclass". Do I have to somehow tell the form that is where the Imagebase object is defined?

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Olaf,
That helped. I put it just above the code in the INIT of the form, is that the best place for it?
I noticed in the Help File there is a "RELEASE CLASSLIB" THis is contained only in a small form, but should I issue a RELEASE CLASSLIB in the DESTROY() event of the Form?

This is showing the correct image in the grid, but display is a little mucked up, but I should be able to resolve that.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
OK, glad you're there.

Where you put the code depends on your needs. You know which events occur when, The grid could also do it's own init, it would have needed yet another class definition to write this in PRG code to share in a forum post. I merely do visual classes and PRG clases have a low importance other than those only available there, eg think of Session. But this leading off track.

The imagecontrol should be in your baseclasses.vcx, OK, and then you can even put this into the grid at design time without code. Just like any other control.

Bye, Olaf.
 
Olaf,
Thanks. I know my stupid questions and lack of understanding on the finer points frustrate you, (it frustrates me too). I know what I want the application behavior to be, but it is sometimes a very complicated hill to climb. Now that I have this one working, I can repeat it in other parts of my application. Many thanks.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Hi Olaf,
So it's very close now. I thought I had it down, but I think there is something I still need.

So with the GRID control set without a control source, My two leading fields "File Description" and "File Extension" are not reflecting what is in the table.
So there are 3 columns:
File Description File Extension Icon

All are held in a table called CTFILETYPES. If I set the controlsource of the GRID to CTFILETYPES, it displays the description and extension perfect, but only displays the image of the first record in each of the grid rows (in this case PDF icon). If I set that off, File Description shows "Memo" in the gird (mouse hover over it shows it's the memo field that contains the pathname of the image in Icon column). Extension is empty, and the icon shows exactly the right Icon.

Do I need to control each individual columns control source? That seems wrong, but I'm not sure how to resolve this conflict.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Most prbably you need to set the Sparse property,see my code.

Bye, Olaf.
 
I set this in the form INIT:

Code:
WITH ThisForm.grdCodeField.Column1
    .Sparse = .F.
ENDWITH
*
WITH ThisForm.grdCodeField.Column2
    .Sparse = .F.
ENDWITH
*
WITH ThisForm.grdCodeField.Column3
	.AddObject("Imagebase1","Imagebase")
    .CurrentControl = 'Imagebase1'
    .Width = 60
    .Sparse = .F.
    .Imagebase1.ImageFilename = "myImageList.FileName"
    .Imagebase1.Visible = .T.
ENDWITH

But the result is the same. MEMO in first column , blank 2nd column, icon in 3rd column.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top