I am building a data entry form using a word document. The logic of why this is a good choice is not relevant. My problem was that I needed to add a set of combo boxes in table cells, each loaded with a list of option for the user to pick from.
There is the option to hand enter each list item... but as I had about 100 items and a dozen duplicate combo boxes... this would have been painful. The fact that I would need to update this list in the future just made it torture. So... is there a faster way?
After some googling without any success, I came up with a very quick and dirty method.
My over view is:
I have the list of items I want in the combo box in a text file in the same directory as the word document. (Note that each item must be Unique as the combo boxes do not like duplicates)
I have built the word document with the Content Control Combo boxes in the correct locations and saved it to the same directory as the text file.
I then added a new module to the word doc via the VBA Editor in Word and wrote this simple little macro.
Option Explicit
Sub PopulateCareProviderComboBoxes()
Dim itemFromFile As String
Dim cc As ContentControl
For Each cc In ActiveDocument.ContentControls
If cc.Type = wdContentControlComboBox Then 'checks if the control is a combo box
cc.DropdownListEntries.Clear 'get rid of any existing entries in the combo box
Open "CareProviderDumpList.txt" For Input As #1 'this is my text file with the items I want to add
Do While Not EOF(1)
Input #1, itemFromFile
cc.DropdownListEntries.Add itemFromFile 'put the item from the text file into the combo box
Loop
Close #1
End If
Next
End Sub
I only needed to check if the control is a combo box, as all the combo boxes on my form are the same. You may need to identify a specific combo box. You can do this by testing the Title property on the Content Control object.
E.g:
If cc.Title = "Customers" then ....
Once thats done, I simply ran the macro to load the list from the text file to each of the combo boxes. Then saved the word doc with the new items in place.
Job done.
Showing posts with label VBA. Show all posts
Showing posts with label VBA. Show all posts
Tuesday, November 6, 2012
Tuesday, November 22, 2011
How to add line breaks to ControlTip Text in Access
Whats the problem?
If you've ever tried to add line breaks or formatting to the ControlTip Text in a control in an Access project you will have noticed that you can add anything to the ControlTip Text property field and it will get put on a single line. I.e...
You can add any sort of tricky string to the property dialog box and get no useful result.
If you have spent any time thinking about property dialog programming you should already see whats going on... input validation. The code behind the property dialog is "sanitizing" your input to prevent you injecting control characters, illegal characters and other types of idiot/malicious code. This is a good thing... except when you want to do something like this intentionally.
Whats the solution?
The above description helped me identify the solution... avoid the input validation behind the property dialog rather than try to bend it to my will. The hack is to add the formatted string for the ControlTip Text via code. I use the Form_Load sub to set tricky control tip text for all the controls on the form that I want to have multi-line ControlTip Text.
I.e
Private Sub Form_Load()
showTaskManager.ControlTipText = "Display the Batch Task Manager" & vbCrLf & "Some other text here"
End Sub
This will show up as a nicely formatted multi-line ControlTip.
Simple once you see it....
If you've ever tried to add line breaks or formatting to the ControlTip Text in a control in an Access project you will have noticed that you can add anything to the ControlTip Text property field and it will get put on a single line. I.e...
You can add any sort of tricky string to the property dialog box and get no useful result.
If you have spent any time thinking about property dialog programming you should already see whats going on... input validation. The code behind the property dialog is "sanitizing" your input to prevent you injecting control characters, illegal characters and other types of idiot/malicious code. This is a good thing... except when you want to do something like this intentionally.
Whats the solution?
The above description helped me identify the solution... avoid the input validation behind the property dialog rather than try to bend it to my will. The hack is to add the formatted string for the ControlTip Text via code. I use the Form_Load sub to set tricky control tip text for all the controls on the form that I want to have multi-line ControlTip Text.
I.e
Private Sub Form_Load()
showTaskManager.ControlTipText = "Display the Batch Task Manager" & vbCrLf & "Some other text here"
End Sub
This will show up as a nicely formatted multi-line ControlTip.
Simple once you see it....
Labels:
Access,
Programming,
Software Tips,
VBA
Thursday, February 10, 2011
Using querydefs and parameter querys in VBA
Today I will be bitching about using parametrized query's from VBA code in a robust way. I will also be presenting a roundup of the solutions as far as I know them.
The Problem
You want to modify a query on the fly using some dynamic option or parameter in VBA and then use that as the RecordSource for a form. Bloody obvious need if one is building a db driven app.
Possible Solutions
The first is to user a parameter query with a field somewhere on a form/global variable that the query can just pick up and run with. This sucks because its inflexible. I need to essentially plant a global somewhere that always exists and always contains the right value... can anyone say spagetti code? Hello 1980.
The second ( variation) is to try to plug the parameter in on-the-fly by massaging the Querydef object and then pulling the Recordset by hand and somehow pushing it into the form or whatever you are trying to use it as a source. This is just fuggly.
The third is to compose the SQL as a string and put the parameter into the string using string functions. Nice, easy and completely unmaintainable using all the nice GUI tools provided in Access. Yes I can do it, but it means I end up with my code littered with SQL fragments that I have to then hand maintain every time something changes in the db schema. what a PITA.
The fourth possible solution I have seen floating around is to build a whole dialog that can compose the SQL on the fly using some combo boxes etc. This is just a variation on solutions 1 and 3. With the worst parts of both. Now you have even more trash to maintain and find when you change anything. The whole point is to reduce complexity not add to it. Blahhhhh.
Fifth possible solution is to roll my own object that somehow can be instantiated, wrap a Querydef object and gracefully update it as required. Since there are no useful hooks to build this around, it gets back to having a global object somewhere and messing with it on demand... back to option 1 but with the overhead of a whole class of code to now maintain.
Main Bitch
What I would much prefer is a nice wrapper object around a Querydef that could be used to create a temporary Querydef with parameter slots, I could then plug in the params, test that the Querydef is stable, and pass it to the form as its Recordsource. This way I can maintain the Querydef using the Querybuilder GUI and all the other test tools, use the QueryDef gracefully from code and not littler my code with all sorts of fragile text string and maintenance headaches. Oh and I want MS to supply this wrapper so I don't need to roll my own. Bastards.
Usually when I find myself banging my head against what seems to be an obvious problem, I usually find that its just that I don't know the "Right way" to do things in that language or framework and that with a little bit of reading I can find it and get my head right (usually refactor a truck load of code) but get back on the true path and start making progress again. However in this case it seems like its a common problem that doesn't really have any particular "right solution" its just lots of ugly hacks. (There are some truly UGLY solutions floating around the forums to this problem. But I don't feel like I have a more pleasing solution so its much of a muchness. Still they offend my eye in ways that indicate they are fragile, hard to maintain and error prone)
Ok head is a bit clearer... bitching complete. I need to go read up on the QueryDef object I think.
Edit:
The best solution I have come up with ( tip of the hat to a forum post I read but can't remember... its probably common knowledge anyway) is to maintain the query in as a normal QueryDef which gives me the ability to use the GUI and compile the SQL for testing; the Query contains a parameter which is essentially a unique key phrase that I then use the "Replace" function to replace with my desired parameter info.
For instance
Dim qdef As QueryDef
'contains a param "[ContID]"
Set qdef = CurrentDb.QueryDefs("ContinuityRelationshipFormSpecificQuery")
Dim theSql As String
theSql = Replace(qdef.sql, "[ContID]", str(relationshipID))
Me.RecordSource = theSql
This is a fairly simple and maintainable system. It takes about four lines to do what I would like to do in less, but its still only a line per parameter so not too horrible a trade-off.
The Problem
You want to modify a query on the fly using some dynamic option or parameter in VBA and then use that as the RecordSource for a form. Bloody obvious need if one is building a db driven app.
Possible Solutions
The first is to user a parameter query with a field somewhere on a form/global variable that the query can just pick up and run with. This sucks because its inflexible. I need to essentially plant a global somewhere that always exists and always contains the right value... can anyone say spagetti code? Hello 1980.
The second ( variation) is to try to plug the parameter in on-the-fly by massaging the Querydef object and then pulling the Recordset by hand and somehow pushing it into the form or whatever you are trying to use it as a source. This is just fuggly.
The third is to compose the SQL as a string and put the parameter into the string using string functions. Nice, easy and completely unmaintainable using all the nice GUI tools provided in Access. Yes I can do it, but it means I end up with my code littered with SQL fragments that I have to then hand maintain every time something changes in the db schema. what a PITA.
The fourth possible solution I have seen floating around is to build a whole dialog that can compose the SQL on the fly using some combo boxes etc. This is just a variation on solutions 1 and 3. With the worst parts of both. Now you have even more trash to maintain and find when you change anything. The whole point is to reduce complexity not add to it. Blahhhhh.
Fifth possible solution is to roll my own object that somehow can be instantiated, wrap a Querydef object and gracefully update it as required. Since there are no useful hooks to build this around, it gets back to having a global object somewhere and messing with it on demand... back to option 1 but with the overhead of a whole class of code to now maintain.
Main Bitch
What I would much prefer is a nice wrapper object around a Querydef that could be used to create a temporary Querydef with parameter slots, I could then plug in the params, test that the Querydef is stable, and pass it to the form as its Recordsource. This way I can maintain the Querydef using the Querybuilder GUI and all the other test tools, use the QueryDef gracefully from code and not littler my code with all sorts of fragile text string and maintenance headaches. Oh and I want MS to supply this wrapper so I don't need to roll my own. Bastards.
Usually when I find myself banging my head against what seems to be an obvious problem, I usually find that its just that I don't know the "Right way" to do things in that language or framework and that with a little bit of reading I can find it and get my head right (usually refactor a truck load of code) but get back on the true path and start making progress again. However in this case it seems like its a common problem that doesn't really have any particular "right solution" its just lots of ugly hacks. (There are some truly UGLY solutions floating around the forums to this problem. But I don't feel like I have a more pleasing solution so its much of a muchness. Still they offend my eye in ways that indicate they are fragile, hard to maintain and error prone)
Ok head is a bit clearer... bitching complete. I need to go read up on the QueryDef object I think.
Edit:
The best solution I have come up with ( tip of the hat to a forum post I read but can't remember... its probably common knowledge anyway) is to maintain the query in as a normal QueryDef which gives me the ability to use the GUI and compile the SQL for testing; the Query contains a parameter which is essentially a unique key phrase that I then use the "Replace" function to replace with my desired parameter info.
For instance
Dim qdef As QueryDef
'contains a param "[ContID]"
Set qdef = CurrentDb.QueryDefs("ContinuityRelationshipFormSpecificQuery")
Dim theSql As String
theSql = Replace(qdef.sql, "[ContID]", str(relationshipID))
Me.RecordSource = theSql
This is a fairly simple and maintainable system. It takes about four lines to do what I would like to do in less, but its still only a line per parameter so not too horrible a trade-off.
Labels:
Access,
Programming,
SQL,
VBA
Friday, October 29, 2010
Profiler for VBA code in Access
I found a nice little stack style Profiler for VBA from a book called Access Cookbook. Its been very handy for identifying and tracking down a couple of memory leaks (My fault as usual) The Profiler is in a module called basProfiler and is discussed in an article called "Create an Execution Time Profiler" which has been reprinted all over the web. I had to fix a small bug in the code that formatted the output to the log file. Other than that it was good to go out of the box.
My usage of basProfiler
1. Import the module.
2. Set the path to the log file to somewhere conveinent. (I put mine into my working directory for subversion to manage)
2. Instrument the code blocks with the opening and closing lines.
I open the log file in Notepad++ on the other monitor and every time I run the code, Notepad++ detects that the file has changed on disk and allows me to reload it. This gives me the power of a real text editor rather than a log window to play with the output.
Simple and elegant. Just the way I like my tools.
I considered hacking it to output a comma delimited file for import into excel for analysis but I ended up not needing that sort of power on this job.
And we are done.
My usage of basProfiler
1. Import the module.
2. Set the path to the log file to somewhere conveinent. (I put mine into my working directory for subversion to manage)
2. Instrument the code blocks with the opening and closing lines.
Public Sub Whatever()
acbProPushStack "Whatever" 'profiler
'code to be profiled here
'.....
acbProPopStack 'profiler
End Sub I open the log file in Notepad++ on the other monitor and every time I run the code, Notepad++ detects that the file has changed on disk and allows me to reload it. This gives me the power of a real text editor rather than a log window to play with the output.
Simple and elegant. Just the way I like my tools.
I considered hacking it to output a comma delimited file for import into excel for analysis but I ended up not needing that sort of power on this job.
And we are done.
Labels:
Access,
Programming Tools,
VBA
Quicksort for VBA array of custom objects
Can you believe that there is no sort function for the collection object in Access VBA? Anyway, I have dumped the collection for a typed array to avoid some of the overhead of type casting variants everywhere. (Long story involving a profiler and a lot of time which finally resulted in finding a different source for the bug that was causing the slow down.)
Back to the main thread. I went looking for a good implementation of a sorting algorithm in VBA. After a couple of absolutly spectacular crappy implementations I found this one.
http://www.java2s.com/Code/VBA-Excel-Access-Word/Data-Type/QuickSort2.htm
Which is short, simple and elegant. In comparison to some of the other multi-page examples I found its really ... beautiful.
Anyway, the first thing I did was refactor out the comparison operation and the swap operation, so I could apply the operation to an array of custom objects without adding clutter to the quicksort sub. By extracting the comparison operation, I can now sort the objects in the array on a variety of properties. If I get bored I will implement both ascending and decending sort but I don't need it right now.
The most painful part of this is not being able to write a template for the code and make type independent. (Anyone mentions VBA variants as being equivalent and they are outa here ...)
Just thought I would share...
Back to the main thread. I went looking for a good implementation of a sorting algorithm in VBA. After a couple of absolutly spectacular crappy implementations I found this one.
http://www.java2s.com/Code/VBA-Excel-Access-Word/Data-Type/QuickSort2.htm
Which is short, simple and elegant. In comparison to some of the other multi-page examples I found its really ... beautiful.
Anyway, the first thing I did was refactor out the comparison operation and the swap operation, so I could apply the operation to an array of custom objects without adding clutter to the quicksort sub. By extracting the comparison operation, I can now sort the objects in the array on a variety of properties. If I get bored I will implement both ascending and decending sort but I don't need it right now.
The most painful part of this is not being able to write a template for the code and make type independent. (Anyone mentions VBA variants as being equivalent and they are outa here ...)
Just thought I would share...
Labels:
Access,
Programming,
VBA
Subscribe to:
Posts (Atom)

