Item_Load Event: Outlook.ActiveExplorer().Selection is not null but cannot be casted to Selection (throws Exception) why? - outlook-addin

I try to develop an Outlook addin. I registered on the Item_Load event with this code:
Application.ItemLoad += Item_Load;
In the event handler I use this code:
[...]
Outlook.Explorer explorer = this.Application.ActiveExplorer();
if (explorer != null)
{
if (explorer.Selection == null)
{
return;
}
int count = explorer.Selection.Count;
if (count > 0)
{
Outlook.Selection selection = explorer.Selection[1];
This throws an Exception as the explorer.Select[1] does not deliver an Outlook.Selection? How is this possible as Outlook.Explorer.Selection[] is defined as:https://learn.microsoft.com/de-de/office/vba/api/outlook.explorer.selection so it should deliver a Selection?

The Selection property returns a Selection object that contains the item or items that are selected in the explorer window. So the code should like this:
Outlook.Selection selection = explorer.Selection;
But if you use an indexer you get an instance of Outlook items:
Outlook.MailItem mail = explorer.Selection[1] as Outlook.MailItem;
Remember that the ItemLoad event provides an instance of the loaded Outlook item as a parameter to the event handler. So, there is no need to get the currently selected items.

Related

Append to meeting invite body with or without Redemption

We are developing an Outlook VSTO add-in.
Right now I am trying to append some information to a meeting invite the user is in the process of composing. I want the content to appear in the body like what clicking the Teams-meeting button would do, where formatted text and links are appended to the end of the body.
Since the content is HTML and the Outlook Object Model does not expose an HTMLBody property for AppointmentItems, I try to set it via Redemption:
// Dispose logic left out for clarity but everything except outlookApplication and outlookAppointment is disposed after use
Application outlookApplication = ...;
AppointmentItem outlookAppointment = ...; // taken from the open inspector
NameSpace outlookSession = outlookApplication.Session;
RDOSession redemptionSession = RedemptionLoader.new_RDOSession();
redemptionSession.MAPIOBJECT = outlookSession.MAPIOBJECT;
var rdoAppointment = (RDOAppointmentItem)redemptionSession.GetRDOObjectFromOutlookObject(outlookAppointment);
string newBody = transform(rdoAppointment.HTMLBody); // appends content right before HTML </body> tag
rdoAppointment.BodyFormat = (int)OlBodyFormat.olFormatHTML;
rdoAppointment.HTMLBody = newBody;
Problem
The Outlook inspector window is not updating with the appended content. If I try to run the code again, I can see the appended content in the debugger, but not in Outlook.
Things I have tried:
Saving the RDOAppointmentItem
Also adding the content to Body property
Using SafeAppointmentItem instead of RDOAppointmentItem; didn't work because HTMLBody is a read-only property there
Setting PR_HTML via RDOAppointment.Fields
Paste the HTML via WordEditor (see below)
Attempt to use WordEditor
Per suggestion I also attempted to insert the HTML via WordEditor:
// Dispose logic left out for clarity but everything except inspector is disposed after use
string htmlSnippet = ...;
Clipboard.SetText(htmlSnippet, TextDataFormat.Html);
Inspector inspector = ...;
Document wordDoc = inspector.WordEditor;
Range range = wordDoc.Content;
range.Collapse(WdCollapseDirection.wdCollapseEnd);
object placement = WdOLEPlacement.wdInLine;
object dataType = WdPasteDataType.wdPasteHTML;
range.PasteSpecial(Placement: ref placement, DataType: ref dataType);
... but I simply receive the error System.Runtime.InteropServices.COMException (0x800A1066): Kommandoen lykkedes ikke. (= "Command failed").
Instead of PasteSpecial I also tried using PasteAndFormat:
range.PasteAndFormat(WdRecoveryType.wdFormatOriginalFormatting);
... but that also gave System.Runtime.InteropServices.COMException (0x800A1066): Kommandoen lykkedes ikke..
What am I doing wrong here?
EDIT: If I use Clipboard.SetText(htmlSnippet, TextDataFormat.Text); and then use plain range.Paste();, the HTML is inserted at the end of the document as intended (but with the HTML elements inserted literally, so not useful). So the general approach seems to be okay, I just can't seem to get Outlook / Word to translate the HTML.
Version info
Outlook 365 MSO 32-bit
Redemption 5.26
Since the appointment is being displayed, work with the Word Object Model - Inspector.WordEditor returns the Document Word object.
Per Dmitrys suggestion, here is a working solution that:
Shows the inserted content in the inspector window.
Handles HTML content correctly with regards to both links and formatting (as long as you stay within the limited capabilities of Words HTML engine).
using System;
using System.IO;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;
using Word = Microsoft.Office.Interop.Word;
namespace VSTO.AppendHtmlExample
{
public class MyExample
{
public void AppendAsHTMLViaFile(string content)
{
// TODO: Remember to release COM objects range and wordDoc and delete output file in a finally clause
Outlook.Inspector inspector = ...;
string outputFolderPath = ...;
string outputFilePath = Path.Combine(outputFolderPath, "append.html");
Word.Document wordDoc = inspector.WordEditor;
File.WriteAllText(outputFilePath, $"<html><head><meta charset='utf-8'/></head><body>{content}</body></html>", Encoding.UTF8);
Word.Range range = wordDoc.Content;
range.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
object confirmConversions = false;
object link = false;
object attachment = false;
range.InsertFile(fileName,
ConfirmConversions: ref confirmConversions,
Link: ref link,
Attachment: ref attachment);
}
}
}

Event Handler Tabbed Web Browser With GeckoFX

I tried to make a tabbed web browser with geckofx. This is my code to make a new tab:
Dim t As New TabPage
Dim bro As New GeckoWebBrowser
bro.Dock = DockStyle.Fill
t.Text = "New Tab"
t.Controls.Add(bro)
TabMain.TabPages.Add(t)
TabMain.SelectedTab = t
bro.Navigate("http://www.google.com")
Now, how to handle DocumentCompleted and DocumentTitleChanged in GeckoWebBrowser? I want to show message if DocumentCompleted event triggered and change window title if DocumentTitleChanged event triggered.
Not sure about the vb.net syntax, but the principle is simple (and it should be fairly simple to transfer from c# to vb)
So, for example:
1) attach an event handler to your browser
browser.DocumentCompleted += browser_DocumentCompleted;
2) generate the method that will do what you need:
void browser_DocumentCompleted(object sender, Gecko.Events.GeckoDocumentCompletedEventArgs e)
{
GWB browser = (GWB)sender;
if (browser.Document == null) return;
//do anything you wanna do when document is completed
AnyMethodBasedOnBrowser(browser);
//e.g. access GUI elements by property binding
TabTitle = "Complete";
//or even alternatively access the control directly
var TabControl = browser.Parent;
//etc etc
}
That should do the trick for you!

VSTO for Outlook 2010 returning nothing instead of contact?

I'm trying to create a right-click menu on the Outlook 2010 contacts view which returns all phone numbers for a contact
I have put in the following XML:
<contextMenu idMso="ContextMenuContactItem">
<button id="MyContextMenuContactItem"
imageMso="AutoDial"
label="Click to dial"
onAction="OnMyButtonClick"/>
</contextMenu>
And this correctly displays the right click item on the contacts list when I right click. I have so far added the following code to the onAction:
Public Sub OnMyButtonClick(ByVal control As Office.IRibbonControl)
Dim card As Office.IMsoContactCard = TryCast(control.Context, Office.IMsoContactCard)
If card Is Nothing Then
MsgBox("Nothing")
Else
MsgBox("We have a card")
End If
End Sub
The problem is here - and I always get nothing. I need to get 'Business Phone' for example ideally.
Here's some C# code that accesses the ContactItem object you've selected in the Contacts Folder. I realize yours is VB, but since they'll both have access to the same object model, it should be just a matter of changing the syntax.
public void OnMyButtonClick(Office.IRibbonControl control)
{
if (control.Context is Outlook.Selection)
{
Outlook.Selection selected = control.Context as Outlook.Selection;
var x = selected.GetEnumerator();
x.MoveNext();
if (x.Current is Outlook.ContactItem)
{
Outlook.ContactItem card = x.Current as Outlook.ContactItem;
Debug.Print(card.FirstName + "'s phone number: " + card.BusinessTelephoneNumber);
}
}
}

Moving through datarepeater's items

Is there any way to move through datarepeater's items through code, as we run loop and move through the items in a list / combo box?
Thanks
Furqan
The code from Schmelter changes the current row, but this might produce undesired effects since it can update the UI and causes other data-handling events to fire. It's not necessary to change the CurrentItemIndex to loop through the DataRepeaterItems. Each DataRepeaterItem is just a Control object in the DataRepeater.Controls collection. Here is an alternative (in C#):
using Microsoft.VisualBasic.PowerPacks;
foreach ( DataRepeaterItem rowItem in dataRepeater1.Controls )
{
int itemIndex = rowItem.ItemIndex;
// If it's bound, get the underlying data object
object dataItem = BindingSource1.List[itemIndex];
// Add code for each rowItem of the dataItem
// All controls on the DataRepeateItem can be obtained from rowItem.Controls
}
This should work:
For i As Integer = 0 To Me.DataRepeater1.ItemCount -1
Me.DataRepeater1.CurrentItemIndex = i
Dim item As DataRepeaterItem = Me.DataRepeater1.CurrentItem
Next

VSTO - Outlook 2007 - Display form before send message?

I'm new to Outlook add-in programming and not sure if this is possible:
I want to display a pop-up form (or selection) and ask for user input at the time they click Send. Basically, whenever they send out an email (New or Reply), they will be asked to select a value in a dropdown box (list items from a SQL database, preferrably).
Base on their selection, a text message will be appended to the mail's subject.
I did my research and it looks like I should use Form Regions but I'm not sure how can I display a popup/extra form when user click Send.
Also, it looks like Form Regions can be used to extend/replace current VIEW mail form but can I use it for CREATE NEW form?
Thanks for everybody's time.
You can probably add the Item Send event handler in the ThisAddIn Internal Startup method and then in the Item Send Event, call the custom form (a windows form).
In the below sample I call a custom windows form as modal dialog before the email item is send and after the send button is clicked.
private void InternalStartup()
{
this.Application.ItemSend += new ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
if (Item is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem currentItem = Item as Microsoft.Office.Interop.Outlook.MailItem;
Cancel = true;
Forms frmProject = new ProjectForm();;
DialogResult dlgResult = frmProject.ShowDialog();
if (dlgResult == DialogResult.OK)
System.Windows.Forms.SendKeys.Send("%S"); //If dialog result is OK, save and send the email item
else
Cancel = false;
currentItem.Save();
currentItem = null;
}
}