RequirementsThere are three basic pieces of forms and reports that I was really interested in. They are (in no particular order) the layout, code module, and any embedded images. There is also the data associated with them, but this is handled easily by my existing methods for exporting tables, data and queries. So these three items are the ones I really care about tracking. In order to successfully track changes to a form or report, I would need to be able to export all of these elements in a manner that is conductive to source control. Writing out the module information was a piece of cake since they are just normal modules name “Form_” + [Form.Name] or “Report_” + [Report.Name]. Getting the layout and images proved to be more problematic.
Attempt #1: The easy wayMy first attempt was to use the ExportXML method of the Application object. If you recall, this method has the ability to export data, schema and presentation information. When applied to a form or report, it will export the bound data as xml, the schema for the data as an xsd, and the presentation information as a xsl template. It will also copy any images to a directory of your choosing. As an added bonus, if all of these objects are exported, it will also output an html file that will pull all of these files together and display it in a web page with a reasonable facsimile of the original form or report. The trouble with this approach is that it needs all of these files. That means you can only use it on bound forms and reports. If they’re unbound, your out of luck. So I scratched this idea.
Attempt #2: The slightly more complicated wayThen I had an idea: what if I bound the unbound forms and reports before exporting it and then unbind it when I was done. I could easily change the object to be bound to one of the system tables that is guaranteed to exist in every database and just have it output the presentation xsl and images. This actually worked beautifully at first. Then I noticed a second flaw with ExportXML. Not only can it not output unbound forms, it can’t output unbound controls either. Any textbox that was not bound to field in the database was not displayed, which for a form bound to a system table was every single textbox. So another idea bites the dust.
Attempt #3: The robust but heavy coding wayAt this point I final bit the bullet and decided to output the layout information manually. As I knew I would be outputting a lot of XML, the best choice was a quick reference to the MSXML library. This is pretty safe since anyone with IE installed will have at least some version of this installed. So I put together a class that takes either a form or report and generates an IXMLDOMDocument representing that object. It simply loops through all of the controls and all of the properties of each object associated with the form or report and adds an IXMLDOMElement to the document accordingly. It then sends the document through a pretty formatter to get nice line breaks and indenting instead of the default everything-on-one-line layout and writes that to disk. Initially I was afraid that this would be exceptionally slow. But as it turns out, I was able to generate these files pretty quickly.
The next step was getting my hands on the images. This too turned out to be pretty easy. I did break SRP for this step (which I may fix in the near future), but right now my XmlWriter actually copies the images as well. It just looks for any Picture property on a control and if the picture is embedded it copies it to a special folder. This was a little complicated by the fact that, at the time I get to the Picture property, I might not know whether it is an embedded or linked image yet. In the case were it is known, I handle it right then. In the case were I don’t know yet, I defer copying the file until we find out.