Accessing User Defined Fields in Outlook - vba

So, I have a custom email form/message like below and I want to access the "Doc Title:" field value to insert it into the body of the email.
I currently have this code;
Function Item_Send()
Item.Body = Item.Body + UserProperties.Find("TextBox1").Text
End Function
And I've tried multiple variations of this, such as Item.UserProperties.Find(...).Value, Find(...).Value by itself, UserProperties.Find("TextBox1", false).Text, etc.
Research;
CodeProject
MSDN Find Method Documentation
Microsoft Support - How to create an email message form
Microsoft Support - FAQ about custom outlook forms
Microsfot Support - Working with User Defined Fields
I just can't seem to find a solution.
The posted code returns Object requred: 'UserProperties.Find(...)'
If I add in false to the parameters I get; Object doesn't support this property of method: 'UserProperties.Find'
Find by itself gives me Type mismatch: 'Find'
And that's all the error messages I can get to come up. Any help would be greatly appreciated. (I'm using the Script Editor button to write the above code, not the Visual Basic button).

You never check that UserProperties.Find returns null. Change the problematic line to
set prop = Item.UserProperties.Find("TextBox1")
if Not (prop Is Nothing) Then
Item.Body = Item.Body + prop.Value
End If
Also make sure the property name is really "TextBox1", that sounds like a control name. Have a look at the item with OutlookSpy (I am its author): click Item button, select the UserProperties property, click browse, go to the IEnumVariant tab, double click on the property.
You can also click the IMessage button to see the raw MAPI properties.

Related

Outlook VBA - can view email Source Code?

In MS Outlook, I want to loop through each selected email, and for each email, view the source code and check if the source file (as in txt format) contains a certain string "XX". Since using view source code can display the email content into html format and I would like to trace some format which with issues in the text.
Currently, I am doing manually by opening the mail, right click > View source > Ctrl+F to find the string I am looking for.
Is there a view to use VBA to do the action of "view source" in the email?
Dim individualItem As Object
For Each individualItem In Application.ActiveExplorer.Selection
'View Source Code of the email
'Find "XX" in the email body content
If Instr(individualItem.body, "XX") = 1 Then
Msgbox ("Find string!")
End if
Next Message
Thanks.
You are almost right, I see two issues:
1.) There are two distinct properties Body (only text) and HTMLBody.
2.) Instr returns the position of the search token. It can be any positive number if the token is found.
So try:
If Instr(individualItem.HTMLBody, "XX") <> 0 Then
By "source" do you mean the MIME headers of the message? They are stored in the PR_TRANSPORT_MESSAGE_HEADERS property - take a look at the message with OutlookSpy (I am its author - click IMessage button). You can reads that property using MailItem.PropertyAccessor.GetProperty. The DASL name of the PR_TRANSPORT_MESSAGE_HEADERS property "http://schemas.microsoft.com/mapi/proptag/0x007D001F".
Note that full MIME source of a message is never stored by Outlook - MIME is not native Outlook format.

Change access form properties from a function

I'm trying to move the OnClick code from one of the buttons on a form to a function, but I'm having some issues. The button in question, called "Reset", changes the properties of most of the objects on the form. Stuff like:
Me.btnName.Caption = "Caption"
Me.btnName.Visible = True
Me.btnName.Top = 123
Me.btnName.Height = 456
'Etc
When moving this over to a function I can't seem to get this to work. I've tried a few different ways of writing it that I found while searching around, but none of them seem to work. I'm messing with some objects in the main form and some in the subform, so I'll show a few examples of both. Edited to include error messages
Forms("FormName").btnName.Caption = "Caption"
'Application-defined or Object-defined error
Forms("FormName").Controls("btnName").Caption = "Caption"
'Ms Access cannot find the referanced form (I've double and triple checked that it is correct)
Forms("FormName").SubFormName.Form.btnName.Caption = "Caption"
'Application-defined or Object-defined error
Forms!FormName!btnName.Caption = "Caption"
'Cannot find the referenced form
Forms!FormName!SubFormName!btnName.Caption = "Caption"
'Cannot find the field reffered to in your expression
Forms!FormName.Controls!btnName.Caption = "Caption"
'Cannot find the referanced form
Forms!FormName!SubFormName!Form.btnName.Caption = "Caption"
'MS Access can't find the field 'SubFormName'
No matter what I try of these I can't seem to get it to work. Maybe I'm just doing something simple wrong, or maybe you can't change properties like this from a function. Regardless, if anyone knows, I would appreciate the help.
Note that the function I'm trying to use is in a separate module, not in the code behind the form.
So you have a OnClick handler on some form's code-behind, responsible for assigning a bunch of properties for objects that live on that form.
What you have done is called encapsulation: outside code doesn't need to care about the Top and Height properties (and others) of the form's btnName button - truth is, outside code shouldn't even need to care that there's a button on the form.
So you're taking this nicely encapsulated object, and moving code around for no apparent reason.
If you need that functionality to be invoked from the outside, then yes, move it out of the OnClick handler.. but not outside the form's code-behind.
Move it to some Public Sub OnReset() procedure, and if outside code needs to invoke that logic, then have it call theForm.OnReset.
Private Sub ResetButton_Click()
OnReset
End Sub
Public Sub OnReset()
Me.btnName.Caption = "Caption"
Me.btnName.Visible = True
Me.btnName.Top = 123
Me.btnName.Height = 456
'Etc
End sub
That way you leave the implementation details of the form within the form itself, while giving outside code an abstraction to say "I don't care what your buttons are named; I don't care what size they are or what their captions are - but when I say 'Reset', you shall re-initialize whatever values you've got for them" - outside code doesn't need to know what Reset does specifically.
Put it this way: the day you rename that btnName button, would you rather need to simply adjust the form's code-behind, or hunt down every possible place in the entire project that could possibly be changing that button's Caption or whatever other property value?
FYI Foo!Bar.Something is late-bound code, shorthand for Foo.Item("Bar").Something: neither Bar nor Something are validated at compile-time. Contrast with Me.btnName.Whatever, where a typo is immediately picked up by the compiler: early-bound code that the compiler is able to validate should always be preferred.
You need to set the property to something:
Forms!FormName!btnName.Caption = "New Caption"
or, if on a subform:
Forms!FormName!<NameOfSubformCONTROL>.Form!btnName.Caption = "New Caption"

Word 2010 VBA Checkboxes

Hopefully an easy one.
I've got a Word document littered with Checkboxes. Is there any way to access these checkboxes as a collection I can loop through and check/set properties such as ID and Value?
For reference, I've already tried the ActiveDocument.ContentControls collection; it's empty.
I've also tried ActiveDocument.Fields. This actually contains the same number of checkboxes as there are on the page, but I can't cast it to a Checkbox to access the properties I need.
One final question, is it possible to dynamically assign an event handler to the click?
Thank you in advance
The collection of all ActiveX fields embedded in your document (let's say its name is ThisDocument) is the collection ThisDocument.Fields. The items of this collection are objects of class Field.
To query the value of the objects in this collection, you'd use something along the lines of:
ThisDocument.Fields.Item(1).OLEFormat.Object.Value
To add code to the event handlers, just right-click on the ActiveX control, and then select "View Code" from the context menu. You'll get the Click event handler:
Private Sub CheckBox1_Click()
' empty if event handler not set, VBA code otherwise '
End Sub
To insert code dynamically, you need to manipulate the Code Module directly, and add the event handlers line by line. This might be tricky but, basically, you'd do something like:
ThisDocument _
.VBProject _
.VBComponents(ThisDocument.CodeName) _
.CodeModule _
.InsertLines(<<Line>>, <<String>>)
where <<Line>> is where you want to insert (number), and <<String>> is the text that you want to insert. Be careful not to insert in the middle of an existing Sub, Function or custom type definition (obviously).

Can I find out if an AppleScript object has a certain property?

I'm attempting to script BBEdit to make me feel more at home in coming from TextMate. One thing I need to be able to do is see if an object I have a reference to has a particular property.
For instance:
tell application "BBEdit"
tell front window
get selected items
end tell
end tell
This will succeed on a project window, but not on a disk browser window, because the latter does not have a 'selected items' property. How do I see if there is such a property in the object?
Please note: I know how to inspect an object in Script Editor (get properties) to see what properties it has, but I need to know at runtime what they are.
What about the class?
tell application "BBEdit"
if class of window 1 is disk browser window then
# ...
else
# ...
end if
end tell
I don't have bbedit so I can't check, but if different types of windows exist, and each type of window has different properties, then can't you just check the window type first? Then you would know what type of properties you can get. There must be some basic property of a window that tells you its type or kind or whatever that would help you make the decision.
The only solution I have so far is to wrap it in an error handler:
try
set sel to selected items
on error errMsg number errNum
if errNum is -1700 then
-- Code that handles no selected items attribute
return
end
error errMsg number errNum
end try
-- Code that handles when selected items attribute exists
There is a difference between documents and windows in BBEdit. Windows are an element of documents, but only windows have the selection property, so you can check the type of window first and avoid catching errors entirely (and make for cleaner code as a result).
Also, try using the selection property, which is hard property in BBEdit as opposed to "selected items" because selection will always return a usable object, even if only an insertion point.

Why can't I get properties from members of this collection?

I've added some form controls to a collection and can retrieve their properties when I refer to the members by index.
However, when I try to use any properties by referencing members of the collection I see a 'Could not set the ControlSource property. Member not found.' error in the Locals window.
Here is a simplified version of the code:
'Add controls to collection'
For x = 0 To UBound(tabs)
activeTabs.Add Item:=Form.MultiPage.Pages(Val(tabs(x, 1))), _
key:=Form.MultiPage.Pages(Val(tabs(x, 1))).Caption
Next x
'Check name using collection index'
For x = 0 To UBound(tabs)
Debug.Print "Tab name from index: " & activeTabs(x + 1).Caption
Next x
'Check name using collection members'
For Each formTab In activeTabs
Debug.Print "Tab name from collection: " & formTab.Caption
Next formTab
The results in the Immediate window are:
Tab name from index: Caption1
Tab name from index: Caption2
Tab name from collection:
Tab name from collection:
Why does one method work and the other fail?
This is in a standard code module, but I have similar code working just fine from within form modules. Could this have anything to do with it?
Edited to add
formTab was declared as a Control, but I find that if it is declared as an Object then the code works.
This will probably solve my problem, but in the interests of furthering my knowledge I would be grateful for any explanation of this behaviour, particularly with regard to the difference in running the code in the different types of module.
This is a really great question. Your edit at the end of the post reveals a lot about how VBA works and what's going on here. I'm not 100% this what's going on, but I'll explain what I think is happening.
A Collection in VBA (and VB6, for that matter; same code base) is not strongly typed. This means that everything in a collection is technically an "object." In the .NET world (as of .NET 2.0), it's possible to have strongly typed collections so that you could say "everything in this collection is a Control object." In VBA, this isn't possible with a Collection.
In your first iteration, where you are referring to the item indexed in the activeTabs collection, activeTabs(x + 1) is referring to an object. When you tell VBA to look up .Caption of that object, it doesn't know what the underlying type is (I think), so it has to simply look to see if the underlying object type contains a property or method called Caption. As you can see, Tab controls do in fact contain a property called Caption.
In your second interation, where you are doing a For Each loop, I think the problem is that the Control type probably doesn't have a property called Caption, though different types of controls probably do. For example, a text box control probably doesn't have a Caption property whereas as label control does have a Caption property.
You have a few options to fix your second loop. 1) You could declare formTab as a Tab control (I'm not sure exactly what it's called). The Tab control should have a Caption property. 2) If every control in activeTabs is not specifically a Tab control (in which case, you should probably call it activeControls instead of activeTabs), you could check within your loop to see if the formTab is actually a Tab control. If it is, cast it as a Tab control and then call .Caption. Until you cast it as a Tab control, VBA won't know that it has a Caption property since a regular Control object doesn't have a caption property.
In the end, you can get away with using objects as in your first loop and letting the runtime figure out what to do, but that can give really bad performance. In general, it's better to work with your specific types in a strongly-typed language. It also helps to show in your code that you know specifically what you're working with rather than leaving it to the runtime to decide what properties and methods you can work with.