Outlook AddIn for filtering recipients - c#

I want to build an Outlook AddIn in C# that has a button in the calendar ribbon that the user clicks to create a new meeting with one of their employees. We want the user (the manager) to be able to select the employee from a filtered list of only their own employees and not have to search through the entire directory. What is the best way to do this?
addendum:
I did some searching and I came across a potential method for the filter.
I know that the "SelectNamesDialog" function will get me an Address Book dialog box:
Outlook.SelectNamesDialog snd = Application.Session.GetSelectNamesDialog();
I want to combine that with a piece of code I found. I modified it to return the names of all the manager's direct reports (the employees under the manager).
I think I'm on the right track, but I'm uncertain of what to do next. How do I now allow the user to select one of these names through the GetSelectNamesDialog? It is OK if your answer is in psuedocode.
// source: "How to: Get Information About Direct Reports of the Current User's Manager"
// https://msdn.microsoft.com/en-us/library/ff184617.aspx
private List<string> GetManagerDirectReports()
{
List<string> AddressNames = new List<string>();
Outlook.AddressEntry currentUser = Globals.ThisAddIn.Application.Session.CurrentUser.AddressEntry;
if (currentUser.Type == "EX")
{
Outlook.ExchangeUser manager = currentUser.GetExchangeUser().GetExchangeUserManager();
if (manager != null)
{
Outlook.AddressEntries addrEntries = manager.GetDirectReports();
if (addrEntries != null)
{
foreach (Outlook.AddressEntry addrEntry in addrEntries)
{
//System.Windows.Forms.MessageBox.Show(addrEntry.Name);
AddressNames.Add(addrEntry.Name);
}
}
}
}
return AddressNames;
}

Address book won't let you restrict the list to a subset of some users, so you'd need to come up with you own window that prompts the user for the selection from a pre-filtered list.

I now seem to be able to post an answer to my own question.
I added a dropdown in the Form Region and added this code to populate the dropdown with names of the manager's direct reports:
// Get Outlook list of employees who report to manager, using Exchange data.
List<string> mgrAddressNames = GetManagerDirectReports();
if (mgrAddressNames.Count >= 1)
{
try
{
// System.Windows.Forms.BindingSource bindingSource1;
// Create a Binding Source to the ComboBox to make values in ComboBox match the results of the list of direct reports.
System.Windows.Forms.BindingSource bindingSource1 = new System.Windows.Forms.BindingSource();
bindingSource1.DataSource = mgrAddressNames;
EmployeeInvited.DisplayMember = "Value";
EmployeeInvited.ValueMember = "Key";
EmployeeInvited.DataSource = bindingSource1.DataSource;
bindingSource1.Dispose();
(etc)

Related

Making Changes to List Causes DataGridView to show blank

I have a project where a user can add new products or modify existing ones. Products can have parts associated with them. I created a list in the class where my product constructor lives to hold parts for each product.
I'm trying to figure out how to set up that if a user makes changes to the parts that are associated with a product but hits the cancel button then the list reverts to the original list. If they hit the save button after making edits to the products parts list then the updated list is saved and when they open the product again then the updated list displays.
All the code in the file I have is quite a bit but the parts I thought would be most helpful for what I have are...
Filling in the product text boxes with the information on the product. Populating the datagridview CurrentPartsDataGrid with the tempParts list that is a copy of the product.Parts list. Or creating a new empty list if it's a new product that is created.
if (product != null)
{
ProductIdText.Text = product.ID.ToString();
ProductNameText.Text = product.Name.ToString();
InvText.Text = product.QOH.ToString();
PriceText.Text = product.Price.ToString();
InvMinText.Text = product.Min.ToString();
InvMaxText.Text = product.Max.ToString();
tempParts = new BindingList<Part>(product.Parts);
}
else
{
product = new Product();
tempParts = new BindingList<Part>();
}
CurrentPartsDataGrid.DataSource = tempParts;
In the save/cancel button click event methods I have tried doing a for loop or for each loop. I clear the list and then try to repopulate.
Code in save
product.Parts.Clear();
foreach (Part part in tempParts)
{
product.Parts.Add(part);
}
Code in Cancel
tempParts.Clear();
foreach (Part part in product.Parts)
{
tempParts.Add(part);
}
If there is anything else that would be helpful, let me know. I'm new to posting here so don't want to overload the post but also don't want to not provide enough.
Any help on how to fix this would be awesome.
Thanks!
I ended up not needing to do any of the EndEdit() stuff.
In my (if product != null)
I added
tempList = new BindingList<Part>();
for (int i = 0; i < product.Parts.Count; i++)
{
tempList.Add(product.Parts[i]);
}
This created a new templist that when the user selected cancel I set product.Parts = tempList which resolved the issue.

Is it possible to have an autofill combobox (within a datagridview) that also provides a dropdown list of all database items in a column?

So I have a DataGrid that uses data from a MySQL database. I'm trying to make it so that when the user start's typing in the "Items" column the box will autofill/suggest/append as well as allow for a dropdown list of all the elements within a column of the database.
So far I've either managed to provide the autofill feature OR the dropdown list. I can't seem to get both of them to work together. I started wondering if it's even possible, and I have yet to find a solution.
Thus, I made this post. If anyone has any advice that would be helpful. Below are a few of the functions I used to do autofill and dropdown list.
private void invoice_DG_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
string titleText = invoice_DG.Columns[1].HeaderText;
if (titleText.Equals("ITEM"))
{
ComboBox autoText = e.Control as ComboBox;
/*if (autoText != null)
{
autoText.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
//AutoCompleteStringCollection DataCollection = new AutoCompleteStringCollection();
autoText.AutoCompleteCustomSource = get_From_Database();
//autoText.Items.Add(get_From_Database());
}*/
if (e.Control is DataGridViewComboBoxEditingControl)
{
autoText.DropDownStyle = ComboBoxStyle.DropDown;
autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
autoText.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
autoText.AutoCompleteCustomSource = get_From_Database();
}
}
}
private AutoCompleteStringCollection get_From_Database()
{
AutoCompleteStringCollection Coll = new AutoCompleteStringCollection();
string querySelect = "SELECT * FROM Items";
MySqlCommand commandSelect = new MySqlCommand(querySelect, conn);
MySqlDataReader reader = commandSelect.ExecuteReader();
while (reader.Read())
{
string type = reader.ToString();
Coll.Add(type); //data inserted in collection so that it will be autocomplete when you type keywords
if (list_Loaded == false)
{
string item = reader.GetString("name");
ITEM.Items.Add(item);
}
} reader.Close();
list_Loaded = true;
return Coll;
}
NOTE These functions are changing and may not reflect what is to ultimately be accepted. This is just their current state.
I'm trying to make it so that when the user start's typing in the "Items" column the box will autofill/suggest/append as well as allow for a dropdown list of all the elements within a column of the database.
So far I've either managed to provide the autofill feature OR the dropdown list. I can't seem to get both of them to work together
The important thing here is that you wish the user to have the ability to append. Therefor the drop-down control must not be a drop-down list as that will prevent users from entering something that isn't already present in the list. I suggest you change it to a regular drop-down control.
The typical flow is that you:
display the drop-down control after the first key-press and begin populating the dropdown with values (whether that comes from a DB is incidental) filtered on what the user has typed. If there are no matches then this is the start of a new entry and so the entered character becomes a new entry in the list
As more and more characters are entered continue to filter OR append the characters to the new entry (from #1)
When they press Enter/focus away, select the matched filtered record OR if it was a new entry save appropriately (in this case to the DB)
You can see this sort of pattern very easily in Visual Studio with a C# file open and watching how the auto-complete aspect of Intellisense works with C#'s dynamic keyword. Normally C# auto-complete kind of prevents you from auto-completing a method call if that method is not recognised.
dynamic auto-complete acts differently in that it makes no assumptions and by default offers no potential members. However, it does have the concept of adding.
dynamic something = // get a reference to some object
something.Foo (); // Intellisense won't offer the Foo() suggestion.
// It won't result as a compile error either
However if we attempt the same method call again in the same method scope Intellisense steps in and shows us that the method we called before Foo() (at least at coding-time) is available. Of course we won't know for sure until runtime.
something.Foo(); // Foo() NOT available in auto-complete
something.Foo(); // Foo() now available in auto-complete

asp.net Keep selected value on dynamically populated / created radiobuttonlist

I hope this question hasn't been asked before in this manner, if it has, I sadly can't find it!
I'm currently trying to create a User Control in which I want a client to select a questionnaire from a dropdownmenu which is dynamically populated on page_load like this:
DiscoverConnectData(); //gets the items from a database
var formItems = 0;
using (var osContext = new OrganizationServiceContext(_crmAccessor.OrganisationService))
{
var forms = (from f in osContext.CreateQuery("form") select f);
foreach (var form in forms)
{
var name = formulier.GetAttributeValue<string>("name");
var formId = formulier.GetAttributeValue<Guid>("formid");
DropDownList_RP.Items.Insert(formItems, new ListItem(naam, formId.ToString()));
formItems++;
}
}
I've also tried putting the above code inside an if (!Page.IsPostBack), however this didn't fix my problem... Okay, moving on. When the client has selected a questionnaire and hit a button, I have a piece of code that runs through all the questions and answers related to that specific questionnaire in a for each loop and creates Labels and RadioButtonLists for each question. First it loads the questions and answers related to the selected formId:
var curQuestionName = string.Empty;
var lblId = 0;
_formGuid = new Guid(DropDownList_RP.SelectedValue);
DiscoverConnectData();
using (var osContext = new OrganizationServiceContext(_crmAccessor.OrganisationService))
{
_answerList =
(from a in osContext.CreateQuery("answer")
join v in osContext.CreateQuery("question")
on a["question"] equals v["questionid"]
where ((EntityReference)v["form"]).Id == _formGuid
select new AnswerClass
{
AnswerQuestionId = v.GetAttributeValue<Guid>("questionid"),
QuestionName = v.GetAttributeValue<string>("name"),
Name = a.GetAttributeValue<string>("ansname"),
Score = a.GetAttributeValue<int>("score")
}).ToList();
}
After picking up this data and adding it to a list, I create the Questionnaire items:
foreach (var ant in _answerList)
{
if (ant.QuestionName != curQuestionName)
{
var vLabel = new Label();
var qid = "Q" + lblId;
vLabel.ID = qid;
vLabel.Text = ant.QuestionName;
Questionnaire.Controls.Add(vLabel);
_aRadioList = new RadioButtonList();
var rblId = "list" + lblId;
_aRadioList.ID = rblId;
Questionnaire.Controls.Add(_aRadioList);
Questionnaire.Controls.Add(new LiteralControl("<br />"));
curQuestionName = ant.QuestionName;
lblId++;
}
_aRadioList.Items.Add(new ListItem((" " + ant.Name), ant.Score.ToString()));
}
This all works fine and I'm getting a list with questions and answers neatly put underneath each other. So far so good!
However, the big problem I'm having is the losing of data on page postback. Whenever the user selects certain answers and I then want to show them a score, or do anything else - the page reloads and all my dynamically added controls are lost. Now I have fixed that issue by putting the relevant functionality above into a function and calling that again when I press the button to show a score, and this recreates the questions and answers, but I lose all the selectedvalues. I don't specifically even need to keep the controls on the page or anything, I just need to keep the selectedvalues in order to do stuff with them!
I can't create the controls on page_load or page_init because I need user input to know exactly which questionnaire to pull from the database. I can't use "hard-coded" values for the dropdownlist items either, because that's not the functionality we're aiming for. We need to pull it all from the database - which I can't access directly, I need to use the DiscoverConnectData() function.
Please, if anyone has any ideas in order to get the functionality I want (also if it means exploring a whole new 'avenue'), please let me know!

VSTO: How to trigger the Check Names action in Active Mail item

I'm creating a Microsoft Office 2013 Add-in that does some behaviour similar to the Address book in an Active Mail item:
I'm working with Microsoft.Office.Interop.Outlook.MailItem and using the Recipients property, and the Recipients.ResolveAll() method to load and remove addresses to it, everything seems to work fine until I click OK on my loaded from so I send control back to the active mail Item. At this point Outlook goes crazy and doesn't place the addresses properly in the To or the CC, or some of them go missing.
I'm thinking that one solution to the problem could be to trigger the Check Names when the user is ready to click OK and send all the addresses from my form to the Active mail item.
How can I trigger this action / button?
I couldn't find anything that could do that in the MailItem class. I would like something similar to this (but to use from within my poped-out windows form in Outloook):
UPDATE:
This are some bits of my code to add more context:
I use the method AddRecipientToActiveItem for each item that I have. It will verify if it already exists (it was already added), if not it will resolve it and if it is correct add it.
private void AddRecipientToActiveItem(string recipientAddress, Recipients recipientList, OlMailRecipientType recipientType)
{
Recipient recipientObject = default(Recipient);
if (!string.IsNullOrWhiteSpace(recipientAddress) && !EmailRecipientAlreadyExists(recipientAddress, recipientType))
{
recipientObject = recipientList.Add(recipientAddress);
recipientObject.Resolve();
if (recipientObject.Resolved)
{
recipientObject.Type = (int)recipientType;
recipientList.ResolveAll();
}
else
{
recipientObject.Delete();
}
}
}
for this, I have to iterate through each element in the list of recipients and compare by address + type (From,To,CC,BCC) pair:
private bool EmailRecipientAlreadyExists(string fullEmailAddress, OlMailRecipientType recipientType)
{
foreach (Microsoft.Office.Interop.Outlook.Recipient recipientObject in ActiveMailItem.Recipients)
{
if (GetRecipientEmailAddress(recipientObject) != null)
{
if (GetRecipientEmailAddress(recipientObject).Equals(fullEmailAddress) && recipientObject.Type == (int)recipientType)
return true;
}
}
return false;
}
But the user can also add addresses to their email item manually, and some of them exist in the exchange server, but others are simple smtp address, so when comparing I have to handle both scenarios:
private string GetRecipientEmailAddress(Microsoft.Office.Interop.Outlook.Recipient recipientObject)
{
Outlook.ExchangeUser objExchangeUser = null;
if (recipientObject.Address != null && recipientObject.AddressEntry != null)
objExchangeUser = recipientObject.AddressEntry.GetExchangeUser();
if (recipientObject.Address == null && objExchangeUser == null)
return recipientObject.Name;
if (objExchangeUser == null)
return recipientObject.Address;
return objExchangeUser.PrimarySmtpAddress;
}
}
It seems like this what I've done is not enough to keep consistent states of the email addresses when I read them from the mail item and put them in a text box of my form, then add more addresses and send them back to the mail item and close the form, and repeat the process.
The Namespace class provides the GetSelectNamesDialog method which obtains a SelectNamesDialog object for the current session. It displays the Select Names dialog box for the user to select entries from one or more address lists, and returns the selected entries in the collection object specified by the property SelectNamesDialog.Recipients.
Sub SelectRecipients()
Dim oMsg As MailItem
Set oMsg = Application.CreateItem(olMailItem)
Dim oDialog As SelectNamesDialog
Set oDialog = Application.Session.GetSelectNamesDialog
With oDialog
.InitialAddressList = _
Application.Session.GetGlobalAddressList
.Recipients = oMsg.Recipients
If .Display Then
'Recipients Resolved
oMsg.Subject = "Hello"
oMsg.Send
End If
End With
End Sub
The dialog box displayed by SelectNamesDialog.Display is similar to the Select Names dialog box in the Outlook user interface. It observes the size and position settings of the built-in Select Names dialog box. However, its default state does not show Message Recipients above the To, Cc, and Bcc edit boxes. For more information on using the SelectNamesDialog object to display the Select Names dialog box, see Display Names from the Address Book.

Receiving (dragged and) dropped Outlook contacts in C#?

I'm developing an application that needs to perform some processing on the user's Outlook contacts. I'm currently accessing the list of Outlook contacts by iterating over the result of MAPIFolder.Items.Restrict(somefilter), which can be found in Microsoft.Office.Interop.Outlook.
In my application, my user needs to choose several contacts to apply a certain operation on. I would like to add a feature that will allow the user to drag a contact from Outlook and drop it on a certain ListBox in the UI (I work in WPF but this is probably is more generic issue).
I'm very new to C# and WPF - how can I:
Receive a dropped item on a ListBox
Verify it's a ContactItem (or something that wraps ContactItem)
Cast the dropped item to a ContactItem so I can process it
Thanks
I tried this with a TextBox (no difference with a ListBox in practice).
Summary :
Searching in all outlook contacts for the one recieved dragged as text.
The search here is based on the person's FullName.
condition(s):
When you drag a contact, it must show the FullName when selected in outlook. The only catch is when two persons have the same full names!! If it's the case you can try to find a unique identifier for a person by combining ContactItem properties and searching them in the dragged text.
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetData("Text") != null)
{
ApplicationClass app;
MAPIFolder mapif;
string contactStr;
contactStr = e.Data.GetData("Text").ToString();
app = new ApplicationClass();
mapif = app.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderContacts);
foreach (ContactItem tci in mapif.Items)
{
if (contactStr.Contains(tci.FullName))
{
draggedContact = tci; //draggedContact is a global variable for example or a property...
break;
}
}
mapif = null;
app.Quit;
app = null;
GC.Collect();
}
}
of course this code is to be organized-optimized, it's only to explain the method used.
You can try using the Explorer.Selection property combined with GetData("Text") [to ensure it's coming from Outlook, or you can use GetData("Object Descriptor") in the DragOver Event, decode the memory stream, search for "outlook", if not found cancel the drag operation] And why not drag multiple contacts!
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetData("Text") != null)
{
ApplicationClass app;
Explorer exp;
List<ContactItem> draggedContacts;
string contactStr;
contactStr = e.Data.GetData("Text").ToString();
draggedContacts = new List<ContactItem>();
app = new ApplicationClass();
exp = app.ActiveExplorer();
if (exp.CurrentFolder.DefaultItemType == OlItemType.olContactItem)
{
if (exp.Selection != null)
{
foreach (ContactItem ci in exp.Selection)
{
if (contactStr.Contains(ci.FullName))
{
draggedContacts.Add(ci);
}
}
}
}
app = null;
GC.Collect();
}
}
An Outlook contact, when dropped, supports the following formats:
(0): "RenPrivateSourceFolder"
(1): "RenPrivateMessages"
(2): "FileGroupDescriptor"
(3): "FileGroupDescriptorW"
(4): "FileContents"
(5): "Object Descriptor"
(6): "System.String"
(7): "UnicodeText"
(8): "Text"
The most interesting looking one on that list (for me) is Object Descriptor, which then led me to someone with a similar sounding problem:
http://bytes.com/topic/visual-basic-net/answers/527320-drag-drop-outlook-vb-net-richtextbox
Where it looks like in that case, they detect that it's an Outlook drop, and then use the Outlook object model to detect what's currently selected, with the implication that that must be the current drop source.
What you could probably do is accept the drag and drop event in your .wpf app and then get the selected item(s) from outlook and pull them into your application.
Update
Add the Outlook PIA references to you app.
Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.Explorer activeExplorer = app.ActiveExplorer();
Microsoft.Office.Interop.Outlook.Selection currentSelection = activeExplorer.Selection;
Then you can iterate over the currentSelection collection to see what the user has dragged over.

Categories