Writing filter expressions when subscribing to events in Team Foundation Server through API, is a bit of tricky until the syntax mystery is resolved. IEventService is explained in MSDN and there are few overloads for subscribing to an event in TFS. Filter expression of type string can be passed to each of these overloads to create different types of subscription filters.

Following code subscribes to TFS event as SOAP subscription.

01.// Access the Team Foundation Server proxy
02.TfsTeamProjectCollection tfsp = new TfsTeamProjectCollection( new Uri("http://yourtfs:port/tfs/YourProjectCollection"));
04.// Access the Team Foundation Event web service
05.IEventService m_eventServiceProxy = (IEventService)tfsp.GetService(typeof(IEventService));
07.// Subscribe for alerts, specifying SOAP notification as delivery preference.
08.DeliveryPreference pref = new DeliveryPreference();
10.string m_port = "1300"; // can be any available port
12.pref.Address = "http://" + Environment.MachineName + ":" + m_port;
13.pref.Type = DeliveryType.Soap;
14.pref.Schedule = DeliverySchedule.Immediate;
16.//Subscribe with no filter
17.string filter = string.Empty;
19.// Now subscribe using Eventing web service API to WorkItemChangeEvent
20.int m_alertId = m_eventServiceProxy.SubscribeEvent("WorkItemChangedEvent", filter, pref);

The above code creates a subscription like below which will receive all work item changes in a given project collection. Must specify name notification can be ignored here and it works fine.

Filter for a Team Project

1.// Filter for team Project
2.string filter = string.Format("\"PortfolioProject\" = '{0}'", "TemProjectName");

Filter for given Team Project applied.

Filter for a WIT

1.// Filter for work item type (user story for example)
2.string filter = string.Format("\"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{0}'", "User Story");

Filter for User Story work item type applied.

Filter for Team Project and WITs

1.// Filter for team Project & work item types (user story and bug for example)
2.string filter = string.Format("\"PortfolioProject\" = '{0}' AND (\"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{1}' OR \"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{2}')", "TemProjectName", "User Story", "Bug");

Filter for given Team Project and Work Item Types applied.

Filter for Team Project, WIT, and a Field Value Change

1.// Filter for team Project, work item type (Task) and a field value change (Remaining Work changed)
2.string filter = string.Format(
3."\"PortfolioProject\" = '{0}' AND \"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{1}' AND \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/NewValue\" <> \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/OldValue\""
4.                , "TeamProjectName"
5.                , "Task");

Filter for Team Project, Task Work Item, and Remaining Work field value change applied.

The trick here is the use of ChangeFields instead of CoreFields when filtering for a value change.

For example, if filtering for a given state:

1.// Filter for team Project, work item type (Task) having state Active and a field value change (Remaining Work changed)
2.            string filter = string.Format(
3.                "\"PortfolioProject\" = '{0}' AND \"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{1}' AND \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/NewValue\" <> \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/OldValue\" AND \"CoreFields/StringFields/Field[ReferenceName='System.State']/NewValue\" = '{2}'"
4.                , "TeamProjectName"
5.                , "Task"
6.                , "Active");

would give


1.// Filter for team Project, work item type (Task), state changed and a field value change (Remaining Work changed)
2.           string filter = string.Format(
3.               "\"PortfolioProject\" = '{0}' AND \"CoreFields/StringFields/Field[ReferenceName='System.WorkItemType']/NewValue\" = '{1}' AND \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/NewValue\" <> \"ChangedFields/StringFields/Field[ReferenceName='Microsoft.VSTS.Scheduling.RemainingWork']/OldValue\" AND \"ChangedFields/StringFields/Field[ReferenceName='System.State']/NewValue\" <> \"ChangedFields/StringFields/Field[ReferenceName='System.State']/OldValue\""
4.               , "TeamProjectName"
5.               , "Task");

would give

An important difference to note here is the usage of ChangeFields to get any changes and CoreFields to filter for a given value.