1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Diagnostics.CodeAnalysis;
12 using System.Activities.Presentation;
17 // The ContextItemManager class maintains a set of context items. A context
18 // item represents a piece of transient state in a designer.
20 // ContextItems must define an empty constructor. This empty constructor
21 // version of a context item represents its default value, and will be the
22 // value returned from GetItem if the context item manager does not contain
23 // a context item of the requested type.
25 // The ContextItemManager supports context layers. A context layer is a
26 // separation in the set of context items and is useful when providing modal
27 // functions. For example, when switching modes in the designer to show the
28 // tab order layout it may be desirable to disable adding items from the
29 // toolbox and change the user mouse and keyboard gestures to focus on setting
30 // the tab order. Rather than grabbing and storing context items before
31 // replacing them with new values, a developer can simply call CreateLayer.
32 // Once the layer is created, all subsequent context changes go to that layer.
34 // When the developer is done with the layer, as would be the case when a user
35 // switches out of tab order mode, she simply calls Remove on the layer. This
36 // removes all context items that were added to the layer and restores the context
37 // to its previous set of values before the layer was created.
39 [SuppressMessage(FxCop.Category.Naming, FxCop.Rule.IdentifiersShouldHaveCorrectSuffix)]
40 public abstract class ContextItemManager : IEnumerable<ContextItem>
44 // Creates a new ContextItemManager object.
46 protected ContextItemManager()
51 // Returns true if the item manager contains an item of the given type.
53 // <param name="itemType">The type of item to check.</param>
54 // <returns>True if the context contains an instance of this item type.</returns>
55 // <exception cref="ArgumentNullException">if itemType is null.</exception>
56 public abstract bool Contains(Type itemType);
59 // Returns true if the item manager contains an item of the given type.
61 // <typeparam name="TItemType">The type of item to check.</typeparam>
62 // <returns>True if the context contains an instance of this item type.</returns>
63 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
64 public bool Contains<TItemType>() where TItemType : ContextItem
66 return Contains(typeof(TItemType));
70 // Enumerates the context items in the editing context. This enumeration
71 // includes prior layers unless the enumerator hits an isolated layer.
72 // Enumeration is typically not useful in most scenarios but it is provided so
73 // that developers can search in the context and learn what is placed in it.
75 // <returns>An enumeration of context items.</returns>
76 public abstract IEnumerator<ContextItem> GetEnumerator();
79 // Returns an instance of the requested item type. If there is no context
80 // item with the given type, an empty item will be created.
82 // <param name="itemType">The type of item to return.</param>
83 // <returns>A context item of the requested type. If there is no item in the context of this type a default one will be created.</returns>
84 // <exception cref="ArgumentNullException">if itemType is null.</exception>
85 public abstract ContextItem GetValue(Type itemType);
88 // Returns an instance of the requested item type. If there is no context
89 // item with the given type, an empty item will be created.
91 // <typeparam name="TItemType">The type of item to return.</typeparam>
92 // <returns>A context item of the requested type. If there is no item in the context of this type a default one will be created.</returns>
93 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
94 public TItemType GetValue<TItemType>() where TItemType : ContextItem
96 return (TItemType)GetValue(typeof(TItemType));
100 // This is a helper method that invokes the protected OnItemChanged
101 // method on ContextItem.
103 // <param name="context">The editing context in use.</param>
104 // <param name="item">The new context item.</param>
105 // <param name="previousItem">The previous context item.</param>
106 // <exception cref="ArgumentNullException">if context, item or previousItem is null.</exception>
107 protected static void NotifyItemChanged(EditingContext context, ContextItem item, ContextItem previousItem)
111 throw FxTrace.Exception.ArgumentNull("context");
115 throw FxTrace.Exception.ArgumentNull("item");
117 if (previousItem == null)
119 throw FxTrace.Exception.ArgumentNull("previousItem");
121 item.InvokeOnItemChanged(context, previousItem);
125 // This sets a context item to the given value. It is illegal to pass
126 // null here. If you want to set a context item to its empty value create
127 // an instance of the item using a default constructor.
129 // <param name="value">The value to set into the context item manager.</param>
130 public abstract void SetValue(ContextItem value);
133 // Adds an event callback that will be invoked with a context item of the given item type changes.
135 // <param name="contextItemType">The type of item you wish to subscribe to.</param>
136 // <param name="callback">A callback that will be invoked when contextItemType changes.</param>
137 // <exception cref="ArgumentNullException">if contextItemType or callback is null.</exception>
138 public abstract void Subscribe(Type contextItemType, SubscribeContextCallback callback);
141 // Adds an event callback that will be invoked with a context item of the given item type changes.
143 // <typeparam name="TContextItemType">The type of item you wish to subscribe to.</typeparam>
144 // <param name="callback">A callback that will be invoked when contextItemType changes.</param>
145 // <exception cref="ArgumentNullException">if callback is null.</exception>
146 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
147 public void Subscribe<TContextItemType>(SubscribeContextCallback<TContextItemType> callback) where TContextItemType : ContextItem
149 if (callback == null)
151 throw FxTrace.Exception.ArgumentNull("callback");
153 SubscribeProxy<TContextItemType> proxy = new SubscribeProxy<TContextItemType>(callback);
154 Subscribe(typeof(TContextItemType), proxy.Callback);
158 // Removes a subscription.
160 // <typeparam name="TContextItemType">The type of context item to remove the callback from.</typeparam>
161 // <param name="callback">The callback to remove.</param>
162 // <exception cref="ArgumentNullException">if callback is null.</exception>
163 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
164 public void Unsubscribe<TContextItemType>(SubscribeContextCallback<TContextItemType> callback) where TContextItemType : ContextItem
166 if (callback == null)
168 throw FxTrace.Exception.ArgumentNull("callback");
170 SubscribeProxy<TContextItemType> proxy = new SubscribeProxy<TContextItemType>(callback);
171 Unsubscribe(typeof(TContextItemType), proxy.Callback);
175 // Removes a subscription.
177 // <param name="contextItemType">The type of context item to remove the callback from.</param>
178 // <param name="callback">The callback to remove.</param>
179 // <exception cref="ArgumentNullException">if contextItemType or callback is null.</exception>
180 public abstract void Unsubscribe(Type contextItemType, SubscribeContextCallback callback);
183 // This is a helper method that returns the target object for a delegate.
184 // If the delegate was created to proxy a generic delegate, this will correctly
185 // return the original object, not the proxy.
187 // <param name="callback">The callback whose target you want.</param>
188 // <exception cref="ArgumentNullException">if callback is null.</exception>
189 // <returns>The target object of the callback.</returns>
190 protected static object GetTarget(Delegate callback)
192 if (callback == null)
194 throw FxTrace.Exception.ArgumentNull("callback");
197 ICallbackProxy proxy = callback.Target as ICallbackProxy;
200 return proxy.OriginalTarget;
203 return callback.Target;
207 // This is a helper method that performs a Delegate.Remove, but knows
208 // how to unwrap delegates that are proxies to generic callbacks. Use
209 // this in your Unsubscribe implementations.
211 // <param name="existing">The existing delegate.</param>
212 // <param name="toRemove">The delegate to be removed from existing.</param>
213 // <returns>The new delegate that should be assigned to existing.</returns>
214 protected static Delegate RemoveCallback(Delegate existing, Delegate toRemove)
216 if (existing == null)
220 if (toRemove == null)
225 ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy;
226 if (toRemoveProxy == null)
228 // The item to be removed is a normal delegate. Just call
230 return Delegate.Remove(existing, toRemove);
233 toRemove = toRemoveProxy.OriginalDelegate;
235 Delegate[] invocationList = existing.GetInvocationList();
236 bool removedItems = false;
238 for (int idx = 0; idx < invocationList.Length; idx++)
240 Delegate item = invocationList[idx];
241 ICallbackProxy itemProxy = item.Target as ICallbackProxy;
242 if (itemProxy != null)
244 item = itemProxy.OriginalDelegate;
247 if (item.Equals(toRemove))
249 invocationList[idx] = null;
256 // We must create a new delegate containing the
257 // invocation list that is is left
259 foreach (Delegate d in invocationList)
263 if (existing == null)
269 existing = Delegate.Combine(existing, d);
279 // Implementation of default IEnumerable.
281 IEnumerator IEnumerable.GetEnumerator()
283 return GetEnumerator();
286 private interface ICallbackProxy
288 Delegate OriginalDelegate
290 object OriginalTarget
295 // This is a simple proxy that converts a non-generic subscribe callback to a generic
298 // <typeparam name="TContextItemType"></typeparam>
299 private class SubscribeProxy<TContextItemType> : ICallbackProxy where TContextItemType : ContextItem
301 private SubscribeContextCallback<TContextItemType> _genericCallback;
303 internal SubscribeProxy(SubscribeContextCallback<TContextItemType> callback)
305 _genericCallback = callback;
308 internal SubscribeContextCallback Callback
311 return new SubscribeContextCallback(SubscribeContext);
315 Delegate ICallbackProxy.OriginalDelegate
317 get { return _genericCallback; }
320 object ICallbackProxy.OriginalTarget
323 return _genericCallback.Target;
327 private void SubscribeContext(ContextItem item)
331 throw FxTrace.Exception.ArgumentNull("item");
333 _genericCallback((TContextItemType)item);