ItemChangeEvent fired multiple times - c#

I work on Outlook addin that is connected to an Owncloud (Caldav, Cardav).
I use eventhandlers to detect when a user delete, create or update a contact item.
In this case this will notify the server and do an update on his side.
storage.Aitems[storage.Aitems.Count - 1].ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(contact_item_add);
storage.Aitems[storage.Aitems.Count - 1].ItemChange += new Outlook.ItemsEvents_ItemChangeEventHandler(contact_item_change);
((MAPIFolderEvents_12_Event)storage.Afolders[storage.Afolders.Count - 1]).BeforeItemMove += new MAPIFolderEvents_12_BeforeItemMoveEventHandler(contact_item_before_move);
When i find new contacts items from the server i add them right the addressbook folder. Outlook detect a new item or a update and then fire the eventshandlers. This is good in case there is only one call (I can say this from the server dont notify again the server or this is from the user notify the server) but i can't because the events are fired multiple times.
static void contact_item_change(object Item) {
Microsoft.Office.Interop.Outlook.ContactItem contact = (Microsoft.Office.Interop.Outlook.ContactItem)Item;
System.Diagnostics.Debug.WriteLine("[Modification contact]" + contact.FullName);
// Need to know if item was created by code (server) or user
Main.SyncContact(contact);
}
Is it possible to know if an item is created trough the GUI or in my code ?
I can't set a variable to know if it was created by user or by my code because of the multiple calls of events.
BTW i already succeed to fire add and delete only one time :
static void contact_item_add(object Item) {
Microsoft.Office.Interop.Outlook.ContactItem contact = (Microsoft.Office.Interop.Outlook.ContactItem)Item;
if (Contact.GetCreatedByUserProperty(contact, "USER")) {
if (storage.PreviousAddedContactId != contact.EntryID)
{
System.Diagnostics.Debug.WriteLine("[Ajout contact]" + contact.FullName);
Main.SyncContact(contact);
storage.PreviousAddedContactId = contact.EntryID;
}
}
}
static void contact_item_before_move(object item, MAPIFolder destinationFolder, ref bool cancel) {
if ((destinationFolder == null) || (IsDeletedItemsFolder(destinationFolder))) {
ContactItem contact = (item as ContactItem);
if (storage.PreviousDeletedContactId != contact.EntryID)
{
System.Diagnostics.Debug.WriteLine("[Suppression du contact]" + contact.FullName);
Main.DeleteContact(contact);
storage.PreviousDeletedContactId = contact.EntryID;
}
}
}
Thank you !

You can read MailItem.LastModificationTime immediately after calling MailItem.Save. When ItemChange event fires, you can compare the new modification time with what you cached and check if it is greater than some delta (1 second?).
In case of Exchange store, you can also retrieve PR_CHANGE_KEY (DASL name http://schemas.microsoft.com/mapi/proptag/0x65E20102) property - it will change after each modification.

Is it possible to know if an item is created trough the GUI or in my code ?
No, there is no property or method which can help in identifying the way which was used to create an Outlook item. But you may add a user property which may indicate that the item was created by your code programmatically. User created item will not have this property set.

I followed the answer of Dmitry Streblechenko
In fact ContactItem have a LastModificationTime property. I don't know if i'm wrong but i guess outlook never change this Datetime when he makes changes. So it's easy to check if user have touch the contact. That allow me to notify the server and it works very well ! Thank you !
static void contact_item_change(object Item) {
Microsoft.Office.Interop.Outlook.ContactItem contact = (Microsoft.Office.Interop.Outlook.ContactItem)Item;
// Need to know if item was created by code (server) or user
var seconds = (DateTime.Now - contact.LastModificationTime).TotalSeconds;
if (seconds < 2) {
System.Diagnostics.Debug.WriteLine("[Modification contact]" + contact.FullName);
Main.SyncContact(contact);
}
}

You only need to focus on properties you want on that Item. When ItemChange event
fired, you can compare properties of that Item with your server Item, if they are difference => do Main.SyncContact(contact);

Related

Sitecore How to save items in different languages when saved via pipeline

Using the Sitecore item:saved event handler, I am trying to add items in other languages. So if you say, add a item in one language, it should automatically add the item in the other given languages when saved.
Right now, I can get all the languages from the master database. The problem is when adding a new version of the new language, it triggers itself and therefore recursive adds "infinite" new elements until it crashes. How is it possible to bypass this?
public void OnItemSaved(object sender, EventArgs args)
{
Item savedItem = Event.ExtractParameter(args, 0) as Item;
if (savedItem.Versions.Count == 1)
{
// Allow only non null items and allow only items from the master database |
if (savedItem != null && savedItem.Database.Name.ToLower() == "master")
{
// Do some kind of template validation to limit only the items
if (savedItem.TemplateID == ID.Parse("{template id}"))
{
// Get all the installed languages from the master database
var installedLanguages = LanguageManager.GetLanguages(Database.GetDatabase("master"));
// Go through every language in the list
foreach (var language in installedLanguages)
{
// Copy item to the new language
if (savedItem.Language.Name != language.Name)
{
using (new LanguageSwitcher(language.Name))
{
// Save the new item, but it fails since it triggers itself (the event)
savedItem.Versions.AddVersion();
}
}
}
}
}
}
Try this code:
foreach (var language in installedLanguages)
{
if (savedItem.Language.Name != language.Name)
{
var otherLanguageItem = savedItem.Database.GetItem(savedItem.ID, language);
if (otherLanguageItem.Versions.Count == 0)
{
otherLanguageItem.Versions.AddVersion();
}
}
}
It's been quite a while since I have done any Sitecore work and I don't currently have access to a Sitecore instance for testing out a code sample, but one way to approach this would be to put a collection of updated item ids on the context. The approach (in pseudo code) could be inserted immediately after your template check (to keep less expensive checks earlier than more expensive ones) and would go something like:
- retrieve collection of updated items from context or create
- if collection contains current item id, return immediately
- add current item id to collection
I'd be happy to provide more clarity on how this would look syntactically if needed, but honestly if I write something in C# I prefer to be able to actually see it work

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.

Trigger Sitecore event programmatically

My sitecore website subscribes to the OnSave event of the content change. When I log into the content manager and change any of the fields, then the event is successfully triggered on the website. Now, I'm trying to do the same thing, except trigger this event from a standalone console app.
The code below changes the content and I'm able to see the change on the website, but the OnSave event is not triggered. The edit context is not silent, so it should trigger the event but it isn't.
I also checked the event quote database and the new event is being created but its just not being triggered on the web. Does anyone have a clue on why the event is not being triggered?
Pipeline.Start("initialize", new PipelineArgs(), true);
// get database
string domainUser = #"sitecore\admin";
if (Sitecore.Security.Accounts.User.Exists(domainUser))
{
Sitecore.Security.Accounts.User user =
Sitecore.Security.Accounts.User.FromName(domainUser, false);
using (new Sitecore.Security.Accounts.UserSwitcher(user))
{
var db = Factory.GetDatabase("master");
Item ii = db.SelectSingleItem("/sitecore/content/Home");
using (new EditContext(ii, true, false))
{
String current = ii.Fields["Breadcrumb Title"].Value;
Console.WriteLine("Current value: " + current);
current += " e";
Console.WriteLine("Changing to: " + current);
ii.Fields["Breadcrumb Title"].Value = current;
}
}
}
The use of EditContext is deprecated. The correct and supported way to edit items is to use Item.Editing.BeginEdit() and Item.Editing.EndEdit(). First thing to try is to switch to those, and see if it makes a difference.
Meanwhile; you say the event is not being triggered "on the web". Are you in a multi-server setup? If so, you need to consider the remote events, as "item:saved" would only trigger on the actual server where the event was triggered.

How to disable a checkbox in a checkedlistbox?

I have some items in a CheckedListBox, I want to disable the CheckBox of first item in it.
i.e. I want to disable the first item in the CheckedListBox, because I want to tell the user visually that option is not available.
Combining 2 of the above partial answers worked great for me.
Add your items to the list with:
myCheckedListBox.Items.Add(myItem, myState);
Where myState is CheckState.Indeterminate for items that should be disabled.
Then add an event handler to keep those items from being changed:
myCheckedListBox.ItemCheck += (s, e) => { if (e.CurrentValue == CheckState.Indeterminate) e.NewValue = CheckState.Indeterminate; };
This does not allow you to use 'Indeterminate' in this list for its normal purpose but it does give a look very similar to what one would expect for a disabled item and it provides the correct behavior!
Though this post is pretty old, the last added answer has been submitted in April this year,
and I hope this will help someone.
I was after something similar : a checked list box that behaves like
a lot of installers, which offer a list of options where some features are required and
thus are both checked and disabled.
Thanks to this post (Can I use a DrawItem event handler with a CheckedListBox?)
I managed to do that, subclassing a CheckedListBox control.
As the OP in the linked post states, in the CheckedListBox control the OnDrawItem event is never fired,
so subclassing is necessary.
It's very basic, but it works.
This is what it looks like (the CheckBox above is for comparison) :
NOTE: the disabled item is really disabled : clicking on it has no effects whatsoever (as far as I can tell).
And this is the code :
public class CheckedListBoxDisabledItems : CheckedListBox {
private List<string> _checkedAndDisabledItems = new List<string>();
private List<int> _checkedAndDisabledIndexes = new List<int>();
public void CheckAndDisable(string item) {
_checkedAndDisabledItems.Add(item);
this.Refresh();
}
public void CheckAndDisable(int index) {
_checkedAndDisabledIndexes.Add(index);
this.Refresh();
}
protected override void OnDrawItem(DrawItemEventArgs e) {
string s = Items[e.Index].ToString();
if (_checkedAndDisabledItems.Contains(s) || _checkedAndDisabledIndexes.Contains(e.Index)) {
System.Windows.Forms.VisualStyles.CheckBoxState state = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedDisabled;
Size glyphSize = CheckBoxRenderer.GetGlyphSize(e.Graphics, state);
CheckBoxRenderer.DrawCheckBox(
e.Graphics,
new Point(e.Bounds.X + 1, e.Bounds.Y + 1), // add one pixel to align the check gliph properly
new Rectangle(
new Point(e.Bounds.X + glyphSize.Width + 3, e.Bounds.Y), // add three pixels to align text properly
new Size(e.Bounds.Width - glyphSize.Width, e.Bounds.Height)),
s,
this.Font,
TextFormatFlags.Left, // text is centered by default
false,
state);
}
else {
base.OnDrawItem(e);
}
}
public void ClearDisabledItems() {
_checkedAndDisabledIndexes.Clear();
_checkedAndDisabledItems.Clear();
this.Refresh();
}
}
Use it like this:
checkedListBox.Items.Add("Larry");
checkedListBox.Items.Add("Curly");
checkedListBox.Items.Add("Moe");
// these lines are equivalent
checkedListBox.CheckAndDisable("Larry");
checkedListBox.CheckAndDisable(0);
Hope this can help someone.
Disabling items isn't a great idea, the user will have no good feedback that click the check box won't have any effect. You cannot use custom drawing to make it obvious. Best thing to do is to simply omit the item.
You can however easily defeat the user with the ItemCheck event:
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
if (e.Index == 0) e.NewValue = e.CurrentValue;
}
To disable any particular item use following:
checkedListBox1.SetItemCheckState(0, CheckState.Indeterminate);
SetItemCheckState takes index of item and CheckState Enum
Indeterminate is used to show shaded appearance
I know it has been a while, but I found this in my search for a list box and thought I would add it to the discussion.
If you have a listbox and want to disable all of the checkboxes so they cannot be clicked, but not disable the control so the user can still scroll etc. you can do this:
listbox.SelectionMode = SelectionMode.None
The CheckedListBox will not work in this way. CheckedListBox.Items is a collection of strings so they cannot be "disabled" as such.
Here are some discussions about possible solutions that might help you: here and here.
This works for me:
checkedListBox1.SelectionMode = SelectionMode.None;
Which means no items can be selected
None: No items can be selected.
For more info, you can check it here: SelectionMode Enumeration.
The solution is to use the event ItemChecking:
_myCheckedListBox.ItemChecking += (s, e) => e.Cancel = true;
This will cancel all the checking on every item, but you can always do more refined solution but testing the current .SelectedItem
Here's how I did it in a helpdesk application I wrote:
First, I made it so the check box was greyed out as I added it to the list during form load:
private void frmMain_Load(object sender, EventArgs e)
{
List<string> grpList = new List<string>();
ADSI objADSI = new ADSI();
grpList = objADSI.fetchGroups();
foreach (string group in grpList)
{
if (group == "SpecificGroupName")
{
chkLst.Items.Add(group, CheckState.Indeterminate);
}
else
{
chkLst.Items.Add(group);
}
}
Then I used an event so that when clicked it ensures it stays clicked:
private void chkLst_SelectedIndexChanged(object sender, EventArgs e)
{
if (chkLst.SelectedItem.ToString() == "SpecificGroupName")
{
chkLst.SetItemCheckState(chkLst.SelectedIndex, CheckState.Indeterminate);
}
}
The idea here is that on my form it's set so that the box checks on item click/select. This way I could kill two birds with one stone. I could keep this event from causing problems when the item is first checked and added during form load. Plus making it check on select allows me to use this event instead of the item checked event. Ultimately the idea is to keep it from messing up during the load.
You'll also notice that it doesn't matter what the index number is, that variable is unknown because in my app it's grabbing a list of groups from AD that exist in a specific OU.
As to whether this is a good idea or not, that's dependent on the situation. I have another app where the item to disable is dependent on another setting. In this app I just want the helpdesk to see that this group is required so they don't go removing them from it.
Try Below Code:
Private Sub CheckedListBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles CheckedListBox1.MouseUp
If (Condition) Then
Me.CheckedListBox1.SelectedIndex = -1
End If
End Sub
I think an alternative solution, is using Telerik components.
A RadListControl can give you that option:

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