Toggling the state of a ribbon button in different inspectors

Add-in Express™ Support Service
That's what is more important than anything else

Toggling the state of a ribbon button in different inspectors
 
Adam B




Posts: 49
Joined: 2013-12-20
I have a custom ribbon that I've created that appears in all compose mail inspectors, there is a toggle button in the ribbon that I want to toggle based on the recipients for that particular message.

I have this code that creates a ComposeHelper that will monitor the recipients every 2 seconds

  private void adxRibbonTabCompose_PropertyChanging(object sender, ADXRibbonPropertyChangingEventArgs e)
        {
            Debug.WriteLine("Tab property changing:" + e.PropertyType + ":" + e.Value);
            ADXRibbonTab tab = sender as ADXRibbonTab;
            ADXRibbonGroup group = tab.Controls[0] as ADXRibbonGroup;
            ADXRibbonButton button = group.Controls[0] as ADXRibbonButton;

            Outlook.Inspector inspector = e.Context as Outlook.Inspector;

            Debug.WriteLine("Button is " + button.Caption);
            ComposeHelper helper = new ComposeHelper(inspector, button);
        }


The compose helper keeps a reference to the button around.

Based on which recipients are present, the ComposeHelper does the following:

        private void UpdateButton()
        {
            if (shared)
            {
                button.Image = 0;
                button.Caption = "Shared";
                button.Pressed = true;
            }
            else
            {
                button.Image = 1;
                button.Caption = "Share";
                button.Pressed = false;
            }
        }


The issue that I'm having is that if there are two messages that are being composed at the same time, both compose helpers have references to the same button.

How do I access the individual instance of the button associated with a particular inspector?

Thanks,
Adam
Posted 09 Jan, 2014 19:45:25 Top
Adam B




Posts: 49
Joined: 2013-12-20
I can provide a sample if that is helpful.

I have created a sample, basically, i created a ribbon for a compose inspector with a toggle button in it, then I created two messages.

Toggling the button in one Inspector causes the toggle button in the other inspector to toggle.

Note, in VSTO, the toggle buttons work as separate toggle buttons with separate state. And I can programmatically set them to the state I need them in for each inspector.
Posted 09 Jan, 2014 23:22:59 Top
Adam B




Posts: 49
Joined: 2013-12-20
I did find this thread:
http://www.add-in-express.com/forum/read.php?SHOWALL_1=1&FID=5&TID=8473#nav_start

Does one effectively have to store the state in the user properties of the MailItem and then use that property to update the toggle state?
Posted 10 Jan, 2014 02:31:46 Top
Andrei Smolin


Add-in Express team


Posts: 18825
Joined: 2006-05-11
Adam,

In addition to intercepting the PropertyChanging event for the tab, you also need to intercept the PropertyChanging event for the button. In the event handler you need to check the context and set the button properties as required by the business logic.


Andrei Smolin
Add-in Express Team Leader
Posted 10 Jan, 2014 04:53:07 Top
Adam B




Posts: 49
Joined: 2013-12-20
So I would set the "toggled" state in context on the "OnClick" (or programmatically) and then use that state during the OnPropertyChanging to set the value to the correct value? And the UserProperties of the MailItem are the right place to set it?
Posted 10 Jan, 2014 09:14:02 Top
Andrei Smolin


Add-in Express team


Posts: 18825
Joined: 2006-05-11
Not exactly.

I have an impression that you can "calculate" the state of the button by checking the recipients. If so, you don't need to use a UserProperty: if e.PropertyType = ADXRibbonControlPropertyType.Pressed in the OnPropertyChanging event, invoke that code to set e.Value.


Andrei Smolin
Add-in Express Team Leader
Posted 10 Jan, 2014 10:04:08 Top
Adam B




Posts: 49
Joined: 2013-12-20
I think that I'm close but still having a few issues. I think that they are mostly revolving around the exact process through which the buttons have their values updated.

My application has a toggle button where the two states are:
Pressed = True & Caption = "Shared"
Pressed = False & Caption = "Share"

The state of this button is completely based on the participants. If there is any participant with an email address in a particular domain (lets call it @share.mycorp.com), the button should be pressed and have the caption "Shared"

There are three ways for the participants to be updated:

  • The mail item has default participants (i.e. a reply)
  • User adds address manually
  • User toggles button


I have events and a timer that fire to make sure that any address change will cause the button to be updated.

            mailItem.PropertyChange += mailItem_PropertyChange;
            mailItem.CustomPropertyChange += mailItem_CustomPropertyChange;
            mailItem.BeforeCheckNames += mailItem_BeforeCheckNames;


All these to is call code that will check the recipient state and update the button.

That is where my confusion is coming in.

As I understand it, by setting the state on the button instance, I set the state on all button instances, so then I have to subscribe to the OnPropertyChanging event from the button and decide whether or not I want to apply that change?

So the code looks like this:

UpdateButtonStateFromRecipients

 private void UpdateButton(MailItem mailItem)
        {
            if (IsShared(mailItem))
            {
                //button.Image = 0;
                button.Caption = "Shared";
                button.Pressed = true;
            }
            else
            {
                //button.Image = 1;
                button.Caption = "Share";
                button.Pressed = false;
            }
        }


Without doing anything, this will cause the state of EVERY instance of this Share Toggle Button, in every Compose Inspector to get toggled to this value.

And then I have a second piece of code:
        private void adxRibbonButtonShare_PropertyChanging(object sender, ADXRibbonPropertyChangingEventArgs e)
        {
            Debug.WriteLine("Tab compose property changing:" + e.PropertyType + ":" + e.Value);
            Outlook.Inspector inspector = e.Context as Outlook.Inspector;
            Outlook.MailItem mailItem = inspector.CurrentItem as Outlook.MailItem;

            if (e.PropertyType == AddinExpress.MSO.ADXRibbonControlPropertyType.Caption)
            {
                if (ComposeHelper.MessageIsShared(mailItem))
                {
                    e.Value = "Shared";
                }
                else
                {
                    e.Value = "Share";
                }

            }
            else if (e.PropertyType == AddinExpress.MSO.ADXRibbonControlPropertyType.Pressed)
            {
                if (ComposeHelper.MessageIsShared(mailItem))
                {
                    e.Value = true;
                }
                else
                {
                    e.Value = false;
                }

            }
        }


This code will ensure that when it goes to set the value for the ShareToggleButton, that it sets the value to the correct value?
Posted 10 Jan, 2014 16:22:10 Top
Andrei Smolin


Add-in Express team


Posts: 18825
Joined: 2006-05-11
Hello Adam,

It looks like your logic will work correctly if you stop calling the UpdateButton method.

Adam B writes:
As I understand it, by setting the state on the button instance, I set the state on all button instances, so then I have to subscribe to the OnPropertyChanging event from the button and decide whether or not I want to apply that change?


An instance of the ADXRibbonButton class deals with all instances of the Ribbon button at once. This works in this way. Say you need to set a new caption or modify the button's state. To do this, you invoke the corresponding property of the ADXRibbonButton class. The code behind the property setter method saves the new value to an internal variable and calls the Invalidate method (see below) which informs the Ribbon that all instances of the Ribbon button are invalid now. At an appropriate moment, when the next instance of the Ribbon button needs to be refreshed (say, when you activate another inspector), the Ribbon invokes the callbacks the component specified and the component supplies the Ribbon with the new value. By default, whatever instance of the ribbon button is being renewed by the Ribbon, the component returns the same value. Handling the PropertyChanging event allows modifying this behavior depending on the business logic; for instance, you can provide different values for different inspector windows.

That is, the Ribbon-related logic is this. You have a value, a callback and the Invalidate method. Whenever the Ribbon expects that the value is (or might be) changed, it invokes the callback and you return a value. To inform the Ribbon that the value is updated, you call the Invalidate method, then the Ribbon invokes the callback and you return a new value.

The component implements this logic: by default, it returns the supplied value whenever the Ribbon asks for it. When the value is updated, it issues the Invalidate method; this makes the Ribbon to retrieve the new value. You customize the logic by handling the OnPropertyChanging event.

Oh, just in case you need to call the Invalidate method yourself, please see the IRibbonUI ribbon parameter (in VB.NET this is ribbon As IRibbonUI); you need to store it and call the IRibbonUI.Invalidate* methods when required.

Hope this helps.


Andrei Smolin
Add-in Express Team Leader
Posted 13 Jan, 2014 03:55:22 Top