VSTO - Outlook 2007 - Display form before send message? - vsto

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;
}
}

Related

Cannot enable a Ribbon button programmatically

I developed a VSTO 4 add-in for Excel. It works perfect, however, I have a button placed in the custom tab of its Ribbon control that is initially disabled.
After clicked other ribbon button in my custom tab, I need to enable the initially disabled button.
I tried with:
btnCancelar.Visible = true;
In the Click event of a button, but button is not shown. The strange thing is that when debugging, it still does not appear, but if a MessageBox is shown, the button get visible at last.
I don't understand this behaviour. How can I enable or disable a ribbon button dynamically by code?
I'm not sure what your language is used in your project, but I guess you can tranform it to your own language used. I'll show the example here in C#:
First you need to implement a so called Callback function in the RibbonXML definition:
<button id="buttonSomething" label="Content" size="large" getVisible="EnableControl"/>
then the next step is to implement the Callback function:
public bool EnableControl(IRibbonControl control)
{
return true; // visible ... false = invisible
}
VSTO will trigger the getVisible Callback and depending on the return value enable or disable the visible state (don't forget to remove any Visible property from the RibbonXML, otherwise the Callback is not triggered)
In case of the Ribbon Designer you need to make sure your Click signature is correct, the easies way to do that is by double clicking the button on the ribbon designer. This will create the Click method for you, for instance:
I created a Ribbon with the Ribbon designer and added two buttons. Double clicked the first button to get an empty method like below, and added the code.
private void button1_Click(object sender, RibbonControlEventArgs e)
{
// Toggle button visibility and make sure the button is enabled
// Visible (obviously) makes it visible, while Enabled is grayed if
// false. You don't need this it is Enabled by default, so just for
// demo purposes
button2.Visible = !button2.Visible;
button2.Enabled = button2.Visible;
// Force Ribbon Invalidate ...
this.RibbonUI.Invalidate();
// Long running proces
}
This worked perfectly for me, so if it doesn't work for you please provide more details of your coding.
I have created a workaround to this.
It was simple. Just started the long running process in different thread. That way, cancel button is shown when it should and then hidden after the process ends.
I used this code to launch the process in the Ribbon.cs code:
btnCancelar.Visible = true;
Action action = () => {
Formatter.GenerateNewSheet(Formatter.TargetType.ImpresionEtiquetas, frm.CustomerID, workbook, btnCancelar);
};
System.Threading.Tasks.Task.Factory.StartNew(action);
And inside the process method I have this code:
public static bool GenerateNewSheet(TargetType type, string customerID, Excel.Workbook workbook, Microsoft.Office.Tools.Ribbon.RibbonButton btnCancelar)
{
try
{
_cancelled = false;
InfoLog.ClearLog();
switch (type)
{
case TargetType.ImpresionEtiquetas:
return GenerateTagPrinting(customerID, workbook);
}
return false;
}
finally
{
btnCancelar.Visible = false;
}
}
The interesting thing here I have discovered is that Excel is thread safe, so it was not necessary to add a synchronization mechanism neither when adding rows in the new sheet nor when setting Visible property to false again.
Regards
Jaime

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!

How to reach formRegion_FormRegionInitializing runtime?

Hello I'm trying to show/hide formRegion in outlook 2007 at Runtime. I have it showing/hiding with e.Cancel in FormRegionFactory_FormRegionInitializing, but it only refreshes when users selects another email. How do i reach event at runtime or there is a better way of showing/hiding the region in 2007 outlook.
if (Settings.Default.DisplayWhere == "WebBrowser")
{
e.Cancel = true;
}
else if(Settings.Default.DisplayWhere == "Outlook")
{
e.Cancel = false;
}
Yes, you can use the FormRegionInitializing event for hiding the form region at runtime. See How to: Prevent Outlook from Displaying a Form Region for more information.
Also you can set the Visible property of the FormRegion class:
WindowFormRegionCollection formRegions =
Globals.FormRegions
[Globals.ThisAddIn.Application.ActiveInspector()];
formRegions.FormRegion1.Visible = false;
See Accessing a Form Region at Run Time for more information.
You may find the Creating Outlook Form Regions section in MSDN helpful.

How do I disable opening multiple MDI Child in VB2010

I am trying to create a menu driven application using MDI forms. My problem is that the menu in the MDI parent will create a new child every time i clicked on it. How do i allow only one particular instance of the child to be open for a particular form but allow multiple forms from different menu to be open. For example, I would want a child from "File" to be open along with an "Edit" child. Also, is there a way to close all other forms whenever a new form is open?
You can do all of that my checking the MdiChildren array of your main form. That array will list all of the open MDI children on your form.
You can determine if an instance of a form is open by looping through the array and checking if a form of the type requested is already open.
To close all open forms, just loop through MdiChildren and call Close on all of the forms.
You can check for an existing instance of your form through the MDIChildren collection:
private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
Form2 f = this.MdiChildren.OfType<Form2>().SingleOrDefault();
if (f == null) {
f = new Form2();
f.MdiParent = this;
f.Show();
} else {
f.BringToFront();
}
}
If you want to close any previous open form, you can also just go through the MDIChildren collection:
if (f == null) {
while (this.MdiChildren.Count() > 0) {
this.MdiChildren[0].Dispose();
}
// etc...

How to start SharePoint 2010 workflow from Infopath 2010 code behind?

I have an Infopath 2010 template with 2 buttons: submit and cancel. When the submit button is clicked I the form is saved to a document library in SharePoint 2010 and the corresponding workflow is clicked off. The user can then open the form and cancel the request by clicking on cancel. I would like to start a different workflow when cancel is clicked. Any ideas as to how that could be done?
Thanks
I have not found a method to kick off a workflow specifically from an Infopath form. I did however find a workaround; here's how I set it up:
Added a column to my list/library that will be set to true when the cancel button is selected.
In my infopath form, add my "cancel" button.
Open the control properties for the button, and select the "Rules" action. Close out of the properties dialog.
I added a fomatting rule for the cancel button so it will only display if the first workflow has started. I also disabled all other editing controls as I only wanted the cancel option to be available.
On the Control Tools contextual tab, in the Button group, click Manage Rules.
Add a new Action rule, it should run two actions: first set the value of the column we created in the first step to true; second submit data using the main data connection.
The workflow you want to run when it is cancelled should be set to run on change. As a first step, evaluate the column created above, and if true, continue the worflow. Make sure you set the value back to false so the workflow doesn't run unintentionally.
Hope that helps.
That is not a bad workaround Nostromo but we actually ended up using the out of the box SharePoint web services to start the workflow from InfoPath code behind. Here is the method we developed to do that.
public static void StartWorkflow(string siteUrl, string docUrl,string workflowName, List<string> approvers,string description)
{
var workflow = new Workflow();
workflow.Url = siteUrl+ "/_vti_bin/workflow.asmx";
workflow.Credentials = System.Net.CredentialCache.DefaultCredentials;
XmlNode assocNode = workflow.GetTemplatesForItem(docUrl);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(assocNode.OwnerDocument.NameTable);
nsmgr.AddNamespace("wf", "http://schemas.microsoft.com/sharepoint/soap/workflow/");
XmlDocument doc = new XmlDocument();
Guid templateID = new Guid();
bool workflowFound = false;
XPathNodeIterator rows = assocNode.CreateNavigator().Select("//wf:WorkflowTemplate", nsmgr);
while (rows.MoveNext())
{
if (rows.Current.GetAttribute("Name", "").ToLower() == workflowName.ToLower())
{
doc.LoadXml(rows.Current.SelectSingleNode("wf:AssociationData/wf:string", nsmgr).Value);
XPathNavigator idNode = rows.Current.SelectSingleNode("wf:WorkflowTemplateIdSet", nsmgr);
templateID = new Guid(idNode.GetAttribute("TemplateId", ""));
workflowFound = true;
break;
}
}
if(!workflowFound)
throw new Exception("System couldn't location the workflow with name: " +workflowName);
XmlElement xmlRoot = doc.DocumentElement;
nsmgr = new XmlNamespaceManager(assocNode.OwnerDocument.NameTable);
nsmgr.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD");
xmlRoot.SelectSingleNode("/my:myFields/my:Description", nsmgr).InnerText = description;
XmlNode reviewersNode = xmlRoot.SelectSingleNode("/my:myFields/my:Reviewers", nsmgr);
reviewersNode.InnerXml = "";
foreach (var user in approvers)
{
XmlNode personNode = reviewersNode.AppendChild(doc.CreateElement("my:Person"));
XmlNode accountIdNode = personNode.AppendChild(doc.CreateElement("my:AccountId"));
accountIdNode.InnerText = user;
XmlNode accountTypeNode = accountIdNode.AppendChild(doc.CreateElement("my:AccountType"));
accountTypeNode.InnerText = "User";
}
XmlNode workflowNode = workflow.StartWorkflow(docUrl, templateID, doc.DocumentElement);
}