Today I want to share with you a technique I use for storing and loading button images across my FileMaker database solution. While I’ve used this technique since in FileMaker 11, its become even more powerful with the padding and outer glow features of FileMaker Pro 13. This technique has a few moving parts to setup, but once created it is very easy to add support for additional buttons. Download the attached sample file or create your own solution.
Since global fields are accessible to any layout without having a relationship to the table occurrence the layout is based on, they are excellent for displaying interface elements. Global fields, including global container fields, will retain the value they have at the last time the file was closed when open locally. But when the file is hosted by FileMaker Server, any data you enter in a global field is not retained.
Create a table where you will store all the interface elements. I call it Resource. Then create a global field named z_Load_Globals_Trigger_g. It’s purpose will be revealed shortly.
This technique involves creating a pair of fields for each button. The first is a standard container field and named something like btn_New. It’s global counterpart is named the same with the addition of the suffix _g, so would be btn_New_g for this example.
Then add this auto enter calculation to the global field:
// Get value of field with same name, minus the _g suffix
Let (
[
gName = GetFieldName ( Self ) ;
fieldName = Left ( gName ; Length ( gName ) - 2 )
] ;
Evaluate ( fieldName ; z_Load_Globals_Trigger_g )
)
The above function looks at its own name and removes the _g suffix to determine the field value to load. This function does not need to change with each new button. Simply duplicate a button field and its global equivilant and change their names.
The final piece is to create a script that will run when the file opens. The script just needs these few lines of code:
Go to Layout ["Resources" (Resources)]
Set Field [Resource::z_Load_Globals_Trigger_g; 1]
That’s it. I use png files that have a transparent background. The button’s graduated fill and border comes from the field’s styling. The field uses rounded corners to create a circular field. They even have Hover, Pressed, and In Focus states. And of course, the fields data observes the new field padding settings, so you can easily control the amount of whitespace between the icon and the boarder.
Download the sample file here:
Happy coding.
This is a great tip, thanks Dawn! I was pretty annoyed when FMS lost the ability to set globals by running a server-side script is this is how we set our global graphic fields previously. We may consider switching to this technique but one question I have is regarding performance on startup. I would assume that the action of setting the globals to the stored equivalent is going to result in a little overhead up-front on startup as the graphics will need to be downloaded from server to client to do this. Do you notice much of a performance hit with this method compared to just having the global set offline ?
Hi Daniel. Thanks for you comment. I've been using this technique for several years now. Because the button images are small (usually less then 1 KB), no the performance hit is not really noticable. Even when the globals are set offline, they still will have to be sent to the user the first time they are displayed.
I’ve recently used this same basic idea, except I used a script to set the value to the global field. I like the auto-enter calc idea better though; I think I’m going to use it!
Regarding performance, some of the graphics I was using were quite a bit larger than those in your demo file and I did experience slow start-up times. I was able to speed up the initial file loading by modifying the calculation to only update the global field if it’s content’s were different than the non-global version. It actually made a significant difference. This idea is based on the assumption that most graphics in the global fields contain the same data as their non-global counterpart; if that’s not true for your file, then this method won’t improve start-up time.
Thanks for your comment, Dan…good info.
You could use the new FileMaker Pro 13 function GetContainerAttribute ( sourceField; attributeName ) to compare the name of the stored file against the name locally saved in the global field. Set the attributeName parameter to filename to get the name.
If you did need to loop through the fields, you could do so dynamically by getting the list of fields for your resource table. (See the blogg post Mislav wrote entitled "FileMaker Field Utilization" last month at https://www.soliantconsulting.com/blog/2013/11/filemaker-field-utilization to see a great technique for getting the standard fields of a table.)
Another way to use this technique is to have your OnLastWindowClose script check if the file is open locally and then refresh the globals using the same few script steps I showed in the demo. The script code would be:
Great article – just wondering if it could also be used with repeating container fields and repeating global container fields?
I have a similar resources table but instead of having 1 field for each icon I have a repeating container field and associated repeating global container field which I initialise on startup.
I like the idea of using evaluate with a trigger field to update the globals – haven’t been able to get it to work with repeating fields though. Do you know if this technique could work with repeating fields?
thanks
Very cool. IΓÇÖve been using a similar method for a while (imagine that!), but IΓÇÖd always just placed the local version of each field on a Graphics layout, then had the startup/OnFirstWindowOpen script do a Loop / Go to Next Field / Set Field. ItΓÇÖs also fairly quick, but I like your Evaluate version a bit better. ItΓÇÖs cleaner.
border,not Boarder
Hey Dawn,
great stuff…
But i use another technique to load buttons:
1. Database file (preferences) with container fields where I place my image (small) for my buttons.
2. In that database I set a field called prefs_linker with a value of 1000 (or whatever)
3. In each new database I put the same field prefs_linker with a value of 1000
4. Make a relation between the database and the preference database on the prefs_linker
5. In my database i put the container fields of my preferences database.
In this way I don’t need to do any calculation, I can put fields in different languages for projects which needs to be used in different languages (when a french user logs in, or an english user logs in) I can take these language fields (like labels) …
Greetings Pieter
hello dawn,
i really like your article as a different approach (than mine ;o) to load graphics.
what first strokes me when exploring the example was that you use fieldnames in calculations which i try to avoid at any cost. so i tried to do this with the internal field id but had to realise that some fields do not have an id.
this should, from my understanding, not be possible. would you mind to get back to me to discuss this further?
best
pixi ;o)
p.s. unfortunately i can’t attach my example.
Thanks for your comment, Steve. Unfortunately the auto enter option for a field will only populate the first repetition of a repeating field, as explained in this FileMaker Knowledgebase record:
http://help.filemaker.com/app/answers/detail/a_id/4054/kw/auto-enter%20repeating%20fields/session/L3RpbWUvMTM5MjIzNTU0NS9zaWQvNS1qN2xNTWw%3D
Thanks,
Dawn
Thank you, Dennis. I've made the correction.
Thanks for your comment, Jeff, and sharing your technique.
Thanks for your comment, Pieter, and sharing your technique. I'm not sure what you mean in Step #5…are you simply adding a table occurance of the Preference file's table?
The advantage of having icons and other interface elements in global fields is that they can be displayed on a layout with any context (i.e. based on any table) without requiring a relationship to what I call the Resource table.
Hi Pixi. Thanks for your comment.
I think you have been mislead. It is appropriate to use field names as long as the names are dynamically derived rather then hardcoded. This is the purpose of the GetFieldName ( field ) function.
I will concede that the technique does require each storage field to have a corresponding global field of the nearly identical name. This demonstration uses a naming convention where the corresponding global field adds the suffix "_g" to the field's name. This forumula can easily be modified to support a different convention.
I do not recommend using FileMaker's field ID for this purpose. I don't think there is an easy way to get the value of a field by specifying its field ID. What's more, I think you would still be hardcoding a value to a specific ID or creating a less transparent dependancy that the global field be created immediately following the storage container field.
Happy coding,
Dawn
Hi Dawn,
in step 5 I just put the container field of the preferences database…
hope this helps
Greetings Pieter
hi dawn,
thanks a lot for answering! you are right regarding the dynamically derived fieldnames. this is easy to handle and implement.
getting the field value can be done by using an excellent custom function from fabrice nordmann to get the fieldname back and then feeding this result to “GetField ( fieldname )”.
“GetField ( FM_Name_ID ( “3” ; “F” ; Get ( FileName ); Get ( LayoutName )))”
gives “delete.png” using your example file.
when you now have a custom functions which swaps the field id of the source ( your “btn_Delete” ) to the field id to be set (your “btn_Delete_g” ) you can not only forget about any naming convention but you can easily modify the mapping in the function too.
but of course, as always with filemaker, there are pros and cons for both ways and everyone is free to use whatever he/she/it prefers.
i try to use file internal values as much as i can since some solutions may be further developed by others not following my naming conventionsΓǪ 😉
but the main point of my comment was: do you have an idea why there is no internal id for some fields?
cheers
pixi
This is almost true: “But when the file is hosted by FileMaker Server, any data you enter in a global field is not retained.”
It is more correct to say that changes to globals by a client are not retained. If you follow this approach (below) you can update the value of global containers in a live environment without having to set anything via script.
1. Create a regular container field for the graphic
2. Insert your graphic
3. Now create a calculated field (type container with global storage), which references the field you created in step 1.
4. Log out and back in. The calculated global field now retains the graphic.
If you want to update the graphic, follow these steps:
1. Insert new graphic in container field.
2. Open the calculated container field and modify it in some way. I toggle the checkbox “do not evaluate if all referenced fields are empty” (i.e., if it’s checked, uncheck it; if it’s unchecked, check it). It doesn’t matter if it’s checked or not, just that you change it.
3. Lout out and back in. The calculated global field now retains the new graphic.
Why does this work?
As we all know, the only way to permanently change a global value is to change it locally. Since FileMaker Server is accessing the file locally, you need to make FMS change the value for it to “stick”. The only way to ensure that FMS makes the change is to modify the field’s definition in some way.
Why is this better than the scripted approach?
If you have a large image library, there is a performance penalty associated with loading all of the images into their global field counterparts.
Pingback: Button Image Masks | beezwax blog