Resource mailboxes play host to a number of room resource specific settings and policies, such as the window (in days) in which you can “book” the resource, as well as a many-layered system of policies and permissions which affect who may use them as well as their experience in doing so. Your application may have a need to read and synchronize with these settings.

Retrieving these settings using PowerShell is straightforward, however that doesn’t stop the topic from being covered incessantly by others on the Internet. Although we can call PowerShell cmdlets from code, the only real solution here is to retrieve the settings using the one and only proper and direct interface to Exchange, i.e. MAPI. To that, however, it helps to know how and where these settings actually get stored in Exchange.

This article examines how these room resource specific settings are actually structured and stored in Exchange, and how you can go about retrieving them. The substance of this article is a result of research (necessitated by the lack of official documentation on the subject) into the inner-workings of Exchange in regards to how policies affecting resource mailboxes are organized.

1. Meet the Resource Settings

Before we get into the nitty-gritty, it probably would help to briefly go over exactly what I mean when I refer to “resource mailbox settings”. Using the Exchange Management Console, we can easily view them by navigating to Recipient Configuration -> Mailbox, right clicking on a resource mailbox, and clicking on Properties. Many tabs will be presented to you; however, for the purposes of this article, we will only be concerning ourselves with five of them.

The first is the Resource General tab, which exposes capacity and custom property settings for the resource mailbox:

Resource General via the EMC

Resource General

Next, we have the prominent Resource Policy tab, which features the most numerous and interesting settings:

Resource Policy via the EMC

Resource Policy

Continuing on, we have the Resource Information tab, which is less about “information” and more about binary settings in relation to the resource mailbox:

Resource Information via the EMC

Resource Information

Ever persistent, we move next to the Resource In-Policy Requests tab, which itself does well to explain what it is that it does:

Resource In-Policy Requests via the EMC

Resource In-Policy Requests

Our journey coming to an end, we arrive at the last stop, the Resource Out-Of-Policy Requests tab:

Resource Out-Of-Policy Requests via the EMC

Resource Out-Of-Policy Requests

The settings that were shown in the preceding images are, collectively, what I will be referring to as the resource settings of a resource mailbox. It is our intention to learn how we might be able to exploit these settings during interactions with an Exchange server which operate on a much more basic level than those that take place through the use of the Exchange Management Console.

There are some very large companies out there which make use of resource settings in order to control the access to and use of various company assets; if your product operates in a space which leverages these assets in a similar manner, it may prove to be desirable to be able to synchronize with these settings so their methods of administration need not change so much.

Firstly, we must first determine if what we wish for is indeed possible. As we can see above, we are looking at the settings via the EMC. We also know that we can manipulate these same resource settings from a PowerShell interface.

An example that exists outside of the Exchange family of software is Microsoft’s own Forefront Identity Manager, which also mirrors the interface and settings exposed by the EMC in a similarly looking way.

Taking all of that into account, then, there must be a way for us to get at those values as well.

As an aside: the purpose of this article is not to cover what each and every resource setting does, as there are a billion articles online that do that. Therefore, I’m assuming that the reader is either intimate in regards to the workings of each setting, or that they at least find them to be obvious in their purpose.

2. Where the Resource Settings Call Home

Although the resource settings shown above all seem to be associated with a resource mailbox in some way, we still really have no idea where they actually get stored. Certainly none of them can be categorized as the typical types of data one expects to encounter when aimlessly trawling through a MAPI store, folder, etc.

If it is our intent to be able to retrieve a resource mailbox’s resource settings, then one of the central questions to be answered is where these resource settings are actually stored in Exchange, as well as what the nature and structure of that storage is.

So, where do the resource settings for a particular resource mailbox come from? Well, it may (or may not, depending on your level of jadedness) surprise you to know that these resource settings do not come from any single location, but rather a couple of sources; namely, the address entry associated with and calendar folder contained by the resource mailbox.

Knowing this, let’s group all of the resource settings known to us into two separate enumerations based on each setting’s origination.

2.1 Address Entry Based Settings
  1. Resource capacity
  2. Resource custom properties
  3. Resource type
  4. Resource delegates (although information may be incomplete…see section on Address Entry Based Settings for more information)
2.2 Calendar Folder Based Settings
  1. Enable Resource Booking Attendant
  2. Delete attachments
  3. Delete comments
  4. Delete subject
  5. Delete non-calendar items
  6. Add organizer’s name to subject
  7. Remove private flag on accepted meetings
  8. Send organizer info upon declining of request due to conflicts
  9. Add additional text to response
  10. Additional text
  11. Mark pending requests as tentative
  12. Allow conflicts
  13. Allow recurring meetings
  14. Schedule only during working hours
  15. Enforce scheduling horizon (booking window)
  16. Booking window
  17. Maximum duration
  18. Maximum number of conflicts
  19. Allowed conflict percentage
  20. Forward meeting requests to delegates
  21. In-policy requests from all users automatically approved
  22. List of users whose in-policy requests are automatically approved
  23. In-policy requests from all users can be approved
  24. List of users whose in-policy requests can be approved
  25. Out-of-policy requests from all users can be approved
  26. List of users whose out-of-policy requests can be approved
  27. Resource delegates (most complete set of information, however more work is required in order to get at the data…see the section on Calendar Folder Based Settings for more information)

And there we have a complete listing of all the public resource settings as well as where we might find them. Cool, but simply knowing where they are isn’t going to cut it for us. We need to know exactly where and how each setting is stored in its respective container if we wish to be able to access their value.

The address entry based settings are simply stored as separate, run-of-the-mill, MAPI properties (albeit “non-standard” MAPI properties) directly on the address entry. The calendar folder based settings are a whole other story.

Because it is the simpler of the two, let’s get into the details of the various address entry based settings first.

3. Address Entry Based Settings

Although it would seem to make more sense if all the resource settings were stored in a single location, in fact it is their use which dictates where they can be found. The presence of a resource’s capacity and custom properties settings on its address entry seems to occur merely to accommodate the Room Finder feature one can find in Outlook.

The set of criteria one may use in order to refine resource searches is a direct reflection of what resource settings are exposed on each of the resource mailbox’s associated address entries. This makes sense, because when we’re using the Room Finder, we are searching across address entries in an address list, not message stores.

3.1 Resource Capacity

The capacity of a room resource can be found by accessing the PR_EMS_AB_ROOM_CAPACITY MAPI property located on its associated address entry.

This property is of the type PT_LONG, its canonical name is PidTagAddressBookRoomCapacity, and it has a property tag of 0×08070003.

3.2 Resource Custom Properties

This fun little creature is a bit more complicated than the resource’s capacity property, and it can actually be found in a couple of places on the address entry, with one of the places being more of a reference to the resource’s custom properties as opposed to the actual entity that defines them.

The MAPI property which defines the resource’s custom properties has neither a name nor a canonical name. It certainly has a property tag, however, and that property tag is 0x0806101F.

This property is of the type PT_MV_UNICODE, which basically means that we are dealing with an array of strings, with the property being “multivalued” and all.

This array will be composed of an entry for each custom property as well as an additional entry at the end of the array which reflects the name of the schema which the preceding custom properties fall under. Because the name of the schema that a custom property falls under must match the type of resource it is meant to be assigned to, the name of the schema should typically always be Room in the case of the resource mailbox being of the room type.

For example, if we use the resource mailbox pictured in the screenshots shown in the previous section, we would get an array of the following composition if we were to access its custom properties:

0:AV;1:TV;2:Room
3.3 Resource Type

An important little fact in regards to a resource mailbox is its type. Is it a piece of equipment, or a room!?! Although you can typically rely on room resources being located underneath the All Rooms address list and equipment resources being located underneath the All Equipment address list (there is one of those…I think?), that’s taking way too big of a leap of faith to be considered a sane approach.

The MAPI property housing the resource’s type, much like the property housing its custom properties, has neither a name nor a canonical name. Its property tag, however, is known to us, and it is 0x0808101F.

It too is of the PT_MV_UNICODE type, although I can’t for the life of me figure out why. It should always contain only a single entry which uses a format of ResourceType:[ResourceTypeName] (drop the brackets, obviously!).

In the case of a room resource mailbox, the value of this property should always be ResourceType:Room.

3.4 Resource Delegates

The users assigned as resource delegates to the resource mailbox can indeed be found in the address entry associated with the resource mailbox, however it is not guaranteed to actually be a complete listing of those users.

The EMC, for example, most assuredly does not get the list of users it uses to populate the Resource Delegate list view from the address entry. However, I bring it up because it is a very simple way to get at those delegates, and it has built-in support when using MAPI client interfaces such as Redemption.

For a better way to get at the list of resource delegates, see the Calendar Folder Based Settings section coming up in a little bit.

The delegates for a resource can be found by accessing the PR_EMS_AB_PUBLIC_DELEGATES located on the resource’s associated address entry. Note that it says PUBLIC in the name — there is a good reason for that, as some delegates are indeed “private”.

It is of the type PtypEmbeddedTable, its canonical name is PidTagAddressBookPublicDelegates, and its property tag is 0x8015000D.

If you are using Redemption, you will find this property in the Delegates property exposed by the relevant RDOAddressEntry object instance.

4. Calendar Folder Based Settings

Now that we’ve gone over the relatively few address entry based settings that exist, it is time to move on to the fun stuff.

The majority of the resource settings can actually be found inside its Calendar folder. They do not, however, exist as properties on the calendar MAPI folder itself; rather, they can be found in a special message located in the calendar’s folder-associated information table

The particular message which houses the resource settings is one that can be found in the associated contents table of any calendar folder, regardless of the type of message store that contains it. This special message can be identified by its message class, which is IPM.Configuration.Calendar. Only one will exist in a folder’s associated contents table, making the use of the message class as a filtering agent a valid action.

That should be all the information you need in order to find this file. If you happen to be using Redemption as your MAPI client, however, you can get at the messages stored in the calendar’s associated contents table by accessing the HiddenItems property exposed by the relevant RDOFolder object instance.

Looking at the IPM.Configuration.Calendar message using MFCMAPI with the message of interest highlighted.

Looking at the IPM.Configuration.Calendar message using MFCMAPI

Once you find this message, you’ll see that it is like any other message, in that it has a plethora of MAPI properties. So what are the properties where we can find the values for our resource settings?

Well, if all you do is simply look over all the available properties by their name, you’re going to end up empty handed.

The resource settings, both fortunately and unfortunately, happen to all be stored in a single MAPI property. You may feel that this approach to the storage of the resource settings is a bit strange and out of place in comparison to how data is typically organized in a MAPI store, and I’d have to agree with you. But this is reality, and idle dreaming won’t do well to get us very far in what we wish to do.

The resource settings are stored in a dictionary-like structure which is defined in XML markup. The schema that it uses is…very interesting. I normally like interesting things, but all that serves to do here is make the process of deserializing the data more problematic. We’ll talk a bit more about that in a second; let’s discuss where this data lives first.

You can find the resource settings in the calendar configuration message by accessing the MAPI property named PR_ROAMING_DICTIONARY.

This is a PT_BINARY typed property, its canonical name is PidTagRoamingDictionary, and its property tag is 0x7C070102.

If we consult Exchange Server Protocol documentation, we learn that this particular property is one that contains an entity known as a dictionarystream. This is basically a binary stream which contains a XML document (UTF-8 encoding). The schema that is used for the XML dictionary is as follows:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="dictionary.xsd"
           xmlns="dictionary.xsd"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="UserConfiguration">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Info">
          <xs:complexType>
            <xs:sequence />
            <xs:attribute name="version"
                          type="VersionString">
            </xs:attribute>
          </xs:complexType>
        </xs:element>
        <xs:element name="Data">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="e"
                          minOccurs="0"
                          maxOccurs="unbounded"
                          type="EntryType">
              </xs:element>
            </xs:sequence>
          </xs:complexType>
          <xs:unique name="uniqueKey">
            <xs:selector xpath="e" />
            <xs:field xpath="@k" />
          </xs:unique>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:simpleType name="VersionString">
    <xs:restriction base="xs:string">
      <xs:pattern value=".+\.\d+" />
    </xs:restriction>
  </xs:simpleType>
  <xs:complexType name="EntryType">
    <xs:sequence />
    <xs:attribute name="k"
                  type="ValueString" />
    <xs:attribute name="v"
                  type="ValueString" />
  </xs:complexType>
  <xs:simpleType name="ValueString">

    <xs:restriction>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:pattern value="\d+\-.*" />
        </xs:restriction>
      </xs:simpleType>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

The place where our resource settings live is within the <Data> element, which is where all of the dictionary name-value pairs go. Now, these do look a bit different from what we’re used to from a .NET perspective, but, nevertheless, there is order to be found amongst the madness here.

Each name-value pair consists of an <e> element and has two attributes: k and v. The k attribute basically acts as the key to the entry, thus it is where you can find the name of the particular setting that the name-value pair represents. The v attribute is obviously then the value portion. That is simple enough, however the complexities arrive in how the value is actually expressed with the v attribute.

The value assigned to the v attribute is actually composed of several parts, each of which is delimited from the next by a hyphen. This is known as the ValueString, and it takes on the following shape:

<data type>-<string encoded value>

…at least according to the documentation. In reality, it is typically more complicated than this.

Also according to the documentation, the data type used must be one of three values: 3 (Boolean), 9 (32-bit signed integer), or 18 (string). And once again, the documentation is not exactly on par with reality, as additional data types are indeed used.

In order to cement our understanding of these calendar based resource settings, let’s look at a real dictionarystream coming from a resource mailbox’s IPM.Configuration.Calendar message. Note that this example does not necessarily reflect the values of the settings as per the screenshots shown at the beginning of this article. There is an important reason for this: if a setting is set at its default value, then it will not appear in the dictionary. Here it is:

<?xml version="1.0" encoding="utf-8"?>
<UserConfiguration>
  <Info version="Exchange.12" />
  <Data>
    <e k="18-ForwardRequestsToDelegates" v="3-False" />
    <e k="18-AddOrganizerToSubject" v="3-False" />
    <e k="18-ConflictPercentageAllowed" v="9-12" />
    <e k="18-DeleteComments" v="3-False" />
    <e k="18-DeleteSubject" v="3-False" />
    <e k="18-AutomateProcessing" v="9-2" />
    <e k="18-MaximumDurationInMinutes" v="9-1441" />
    <e k="18-AllowConflicts" v="3-False" />
    <e k="18-RequestInPolicy" v="1-18-1-36-6f33f1c8-ba68-4852-8ba3-a727920a5f39" />
    <e k="18-RemovePrivateProperty" v="3-False" />
    <e k="18-ScheduleOnlyDuringWorkHours" v="3-False" />
    <e k="18-EnforceSchedulingHorizon" v="3-False" />
    <e k="18-TentativePendingApproval" v="3-False" />
    <e k="18-OrganizerInfo" v="3-False" />
    <e k="18-AllRequestOutOfPolicy" v="3-False" />
    <e k="18-BookInPolicy" v="1-18-2-36-b867b454-75c4-480f-aba0-47bd5b11d5b7-36-7461ec96-5929-4905-8f73-a4e0abb1ff8e" />
    <e k="18-MaximumConflictInstances" v="9-5" />
    <e k="18-AllBookInPolicy" v="3-False" />
    <e k="18-AddAdditionalResponse" v="3-True" />
    <e k="18-AdditionalResponse" v="18-Additional Test" />
    <e k="18-DeleteAttachments" v="3-False" />
    <e k="18-DeleteNonCalendarItems" v="3-False" />
    <e k="18-BookingWindowInDays" v="9-181" />
    <e k="18-RequestOutOfPolicy" v="1-18-1-36-b867b454-75c4-480f-aba0-47bd5b11d5b7" />
    <e k="18-AllRequestInPolicy" v="3-False" />
    <e k="18-AllowRecurringMeetings" v="3-False" />
  </Data>
</UserConfiguration>

As we can see above, there exists a name-value pair for every one of the resource settings we previously declared as being calendar folder based. Most of these properties look fairly normal and easy to digest, however there are a few that live outside the scope of official documentation. Particularly, there appear to be a few settings that have a ValueString assigned to their v attribute that contains a data type not found in the official documentation’s table of possible data types.

An example of this is the value assigned to the RequestInPolicy setting.

If we follow the standard format of a ValueString, then the data type being used here is 1. Unfortunately, there is no data type in official documentation that uses this identifier. Fortunately for us, we have brains, and we can quickly deduce that based on the setting itself (which receives a list of users that can make in-policy requests) that the type of data must be some sort of type of collection of values.

OK, so we’re past the data type: this is a list of some kind. Let’s continue down the ValueString. We’ll quickly notice that, also unlike what’s documented, this particular ValueString consists of many more parts than just a data type and actual value. Let’s go through each one and then come up with our own kind of format used for this data type.

Right after the standard data type portion of the value type, we see an 18. This represents something I’ll call the sub-data type, or rather, the type of data for all the members in the collection. This particular sub-data type indicates that the collection is one that contains all string values.

After the sub-data type indicator, we can see a 1. While what this means is not obvious from the current setting we’re looking at, if you look at another setting with more than one user assigned, it quickly becomes apparent: this is an indicator of the number of items in our collection. For this particular setting, the 1 indicates that there is only one string to be found in the collection.

Next, we see a 36. What this represents is the length of the string encoded value. So, in our example, because the string encoded value is 36 characters, we have a 36 for the size indicator. It is important to note that this indicator indicates the length of the string encoded value, not necessarily the actual value. So, if we theoretically happened to have a collection comprised of 32-bit signed integer data, then the number 942 (were it to be a member of the collection) would yield a size indicator of 3.

Going onward, we’ll finally arrive at the first (and only) value for the collection. Perhaps confusingly at first, this value is comprised of multiple hyphens; this is why the length indicator is crucial. There are hyphens in the value because what this value is, is a GUID.

So, with all of that figured out, let’s define the format for ValueStrings using a collection data type:

1-<sub-data type>-<n>{[-<value length>-<string encoded value>](0) ... [-<value length>-<string encoded value>](n-1)}

…with n representing the number of items in the collection.

And there we have it. But not quite: we still don’t know what the heck the GUID values found in these collections even are.

Well, I’ll tell you what they are: each GUID value is the value of the objectGUID property of a user object in Active Directory. Thus, in order to ascertain the identity of a user from these settings, you would need to first bind to their user object using the objectGUID, and then go from there.

This is a sensible approach to storing a reference to a user’s identity; a user’s objectGUID property will never change. In fact, I wish more of the data stored in Exchange took this approach — too often the only reference to the user is the user’s display name.

Still, it will add a slight burden onto you in order to get any user out of it.

4.1 How Am I Supposed to Read This Crap?

Yes, yes…we understand now the structure of the dictionarystream that contains the resource settings, however we probably want to be able to somehow deserialize the raw XML data into some nice and friendly objects.

Well, given the nature of the schema, it would be foolish to expect, as far as .NET XML serialization is concerned, that deserializing this data would be something automatically supported. It has hopefully become more and more obvious to you from reading this article that these resource settings weren’t designed with an expectation that people outside Microsoft would be reading them directly from their points of storage.

One might wonder why it is stored in an XML dictionary at all. If it somehow makes the data more easily “shippable” when responding to Exchange Web Services requests, then that would not surprise me, but I have not examined that particular aspect, so I cannot say.

If you want to read this type of dictionary, there is no other way than to create a class which implements the IXmlSerializable interface. This is, of course, a very non-trivial thing to do. If you need some guidance as to how to do this, then you can refer to Microsoft’s own implementation, which is the internal ConfigurationDictionary class found in the Microsoft.Exchange.Data.Storage assembly.

I’d provide one for you, but frankly, that’s when I would start charging money.

4.2 Resource Delegates

Back in the section on address entry based settings, I mentioned that the resource delegates are not actually read from the address entry by the EMC, and is not the best place to retrieve that information. This is more of a general fact than one that is specific to resource mailboxes.

The reason why the address entry may not be complete in the information it offers, is because it is possible to set up a user so that they are hidden from all address lists. You can find this option on the General tab in the properties window of the user’s mailbox, as pictured below:

User is Hidden and Will Not Be Listed Publicly as a Delegate

If this check box is checked, then the user will not be publicly listed as a delegate, even though it is a delegate.

The best way to get all the delegates for a resource mailbox is by looking at the Access Control List for the resource mailbox’s calendar folder. I’m fairly convinced that this is what the EMC does when it displays the resource delegates for a particular mailbox, and it does this by referring to the calendar folder’s PR_NT_SECURITY_DESCRIPTOR MAPI property. If you are using Redemption, you can do this by accessing the ACL property exposed by the relevant RDOFolder object instance.

However, of course, other permissions which are intended for anything but delegate access may very well be defined in the ACL, so you will have to determine who’s a delegate and who isn’t on an entry by entry basis. I do not know if it is possible for an Exchange administrator to be able to change what the default delegate roles are, but if that is found to be not possible, then you can simply check whether or not the entry has an access mask value of 0x1208AB, which I believe is what gets doled out when someone is assigned delegate permissions (you will want to research that out, however).

4.3 Dictionary Keys for Resource Settings

One last bit of knowledge that may help is to know which name-value pairs in the dictionary represent which resource settings. For the majority of these settings, this is fairly obvious; however, I will provide a table for you because I’m a nice guy.

Resource Setting

Dictionary Key

Enable Resource Booking Attendant AutomateProcessing
Delete attachments DeleteAttachments
Delete comments DeleteComments
Delete subject DeleteSubject
Delete non-calendar items DeleteNonCalendarItems
Add organizer’s name to subject AddOrganizerToSubject
Remove private flag on accepted meetings RemovePrivateProperty
Send organizer info upon declining of request due to conflicts OrganizerInfo
Add additional text to response AddAdditionalResponse
Additional text AdditionalResponse
Mark pending requests as tentative TentativePendingApproval
Allow conflicts AllowConflicts
Allow recurring meetings AllowRecurringMeetings
Schedule only during working hours ScheduleOnlyDuringWorkHours
Enforce scheduling horizon (booking window) EnforceSchedulingHorizon
Booking window BookingWindowInDays
Maximum duration MaximumDurationInMinutes
Maximum number of conflicts MaximumConflictInstances
Allowed conflict percentage ConflictPercentageAllowed
Forward meeting requests to delegates ForwardRequestsToDelegates
In-policy requests from all users automatically approved AllBookInPolicy
List of users whose in-policy requests are automatically approved BookInPolicy
In-policy requests from all users can be approved AllRequestInPolicy
List of users whose in-policy requests can be approved RequestInPolicy
Out-of-policy requests from all users can be approved AllRequestOutOfPolicy
List of users whose out-of-policy requests can be approved RequestOutOfPolicy

Post a comment if you have any questions or anything interesting to say.

 

A reader recently sent me a message requesting further clarification on how one might use the generic IWeakEventListener implementation I recently wrote about (note to self: I need to disable the auto-comment disabling feature on this site).

The Scenario

Let’s say we have a child/parent pair of view models which serve as abstractions of a child/pair pair of views. The child view model mediates between a single item model and the child view, whereas the parent view model mediates between a collection-based item model and the parent view. Naturally, the parent view is going to have some type of ItemsControl living on it, which will, in turn, be generating one or more child view instances.

Now, let’s further assume that a requirement exists that compels us to subscribe to an event made available by the parent view model from somewhere on the child view model’s level. Thus, in this case, the parent view model is the publisher or source and the subscribing element exposed by the child view model is the subscriber or listener. For the purposes of this article, let’s say that it is an ICommand which is exposed by the child view model needs to subscribe to the parent view model’s event, for whatever reason.

Most of the time, in a normal .NET application, this kind of requirement isn’t a big deal. That’s because, unless we’re doing something wrong, our design should be such that the proper mechanism is in place (e.g. IDisposable, etc.) which will ensure that the child object will always be cognizant of when it is no longer needed and capable of responding to that.

The reason why the notion of weak event management suddenly becomes more prevalent or even essential when working with WPF is because of the very fact that WPF eschews various paradigms and practices followed religiously by other parts of the .NET Framework. One implication from this is the greater likelihood of a scenario occurring where our subscriber cannot and will not be made aware of when it should unregister from the events it has subscribed to.

To get around this problem, we make use of the weak event pattern. We already made a generic weak event listener in the previous article, so let’s show how to fully use it.

Using Weak Events

Our subscriber class (that which attaches to the published events and thus holds the event handlers that respond to them) is what will contain an instance of our generic weak event listener. If we follow the scenario shown above, we have a command on the child view model which needs to attach to an event. Let’s assume the parent view model implements an interface named INotifyAboutSomething which indicates its capability of publishing an event named SomethingHappened which, when fired, itself indicates a change in…something.

A Weak Event Manager

Before we can use an instance of our generic weak event listener in the definition of our command, however, we need to create a WeakEventManager for the particular event we want to handle. The event in question is the SomethingHappened event, so let’s define a class named SomethingHappenedEventManager (and just a note, none of the code, in the form that it is being made available, has been tested, although the code it originates from has been).

SomethingHappenedEventManager.cs
/// <summary>
/// Provides a <see cref="WeakEventManager"/> implementation so that you can use the weak event listener
/// pattern to attach listeners for the <see cref="INotifyAboutSomething.SomethingHappened"/> event.
/// </summary>
public sealed class SomethingHappenedEventManager : WeakEventManager
{
    /// <summary>
    /// Gets the currently initialized instance of this specific manager type; if one does not already exist,
    /// then this will create and register a new instance.
    /// </summary>
    private static SomethingHappenedEventManager CurrentManager
    {
        get
        {
            Type managerType = typeof(SomethingHappenedEventManager);

            SomethingHappenedEventManager currentManager
                = (SomethingHappenedEventManager) GetCurrentManager(managerType);

            if (null == currentManager)
            {
                currentManager = new SomethingHappenedEventManager();

                SetCurrentManager(managerType, currentManager);
            }

            return currentManager;
        }
    }

    /// <summary>
    /// Adds the specified listener to the <see cref="INotifyAboutSomething.SomethingHappened"/> event of the
    /// specified source.
    /// </summary>
    /// <param name="source">The <see cref="INotifyAboutSomething"/> object with the event.</param>
    /// <param name="listener">The <see cref="IWeakEventListener"/> object to add as a listener.</param>
    public static void AddListener(
        [NotNull]INotifyAboutSomething source, [NotNull]IWeakEventListener listener)
    {
        CurrentManager.ProtectedAddListener(source, listener);
    }

    /// <summary>
    /// Removes the specified listener from the <see cref="INotifyAboutSomething.SomethingHappened"/> event
    /// of the specified source.
    /// </summary>
    /// <param name="source">The <see cref="INotifyAboutSomething"/> object with the event.</param>
    /// <param name="listener">The <see cref="IWeakEventListener"/> object to remove.</param>
    public static void RemoveListener(INotifyAboutSomething source, [NotNull]IWeakEventListener listener)
    {
        CurrentManager.ProtectedRemoveListener(source, listener);
    }

    /// <inheritdoc/>
    protected override void StartListening(object source)
    {
        INotifyAboutSomething publisher = (INotifyAboutSomething) source;

        publisher.SomethingHappened += HandleSomethingHappened;
    }

    /// <inheritdoc/>
    protected override void StopListening(object source)
    {
        INotifyAboutSomething publisher = (INotifyAboutSomething) source;

        publisher.SomethingHappened -= HandleSomethingHappened;
    }

    /// <summary>
    /// Handles a change in something.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="eventArgs">
    /// The <see cref="SomethingHappenedEventArgs"/> instance containing the event data.
    /// </param>
    private void HandleSomethingHappened(object sender, SomethingHappenedEventArgs eventArgs)
    {
        DeliverEvent(sender, eventArgs);
    }
}

You can create a generic weak event manager if you wish as well, however, for reasons I pointed out in my previous article on the subject, it may not be in your best interest to do so.

Using It All Together

With the weak event manager defined, here’s a skeleton of our command then, showing the usage of our generic weak event listener and the weak event manager we just defined.

TheCommand.cs
/// <summary>
/// Provides a command that does something in response to something.
/// </summary>
public class TheCommand : ICommand
{
    private readonly WeakEventListener<SomethingHappenedEventArgs> _somethingHappenedListener;

    /// <summary>
    /// Initializes a new instance of the <see cref="TheCommand"/> class.
    /// </summary>
    /// <param name="publisher">
    /// The publisher of the event this command is responding to in some fashion.
    /// </param>
    public TheCommand([NotNull]INotifySomethingHappened publisher)
    {
        _somethingHappenedListener = new WeakEventListener<SomethingHappenedEventArgs>(OnSomethingHappened);

        SomethingHappenedEventManager.AddListener(publisher, _somethingHappenedListener);
    }

    /// <summary>
    /// Responds to something happening by...doing something else.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="eventArgs">
    /// The <see cref="SomethingHappenedEventArgs"/> instance containing the event data.
    /// </param>
    private void OnPoolChanged(object sender, SomethingHappenedEventArgs eventArgs)
    {
        // ...
    }
}

The magic above occurs when we make a call to the static SomethingHappenedEventManager.AddListener method, which is going to complete the registration of our weak event listener. Note that there is also a RemoveListener method, which, if possible, we can call in order to be as tidy as possible, but this is not necessary, we’re dealing with weak references here. Although, that wouldn’t excuse us from omitting a call to RemoveListener if a state does indeed exist in which we become aware that it is our time to go. Tidy, tidy!

The code above is obviously missing other parts required of a command, as that has nothing to do with this article (I’m just letting all you friendly copy-pasters out there know).

© 2012-2013 Matt Weber. All Rights Reserved. Terms of Use.