//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Activities.Presentation;
using System.Runtime;
//
// The ContextItemManager class maintains a set of context items. A context
// item represents a piece of transient state in a designer.
//
// ContextItems must define an empty constructor. This empty constructor
// version of a context item represents its default value, and will be the
// value returned from GetItem if the context item manager does not contain
// a context item of the requested type.
//
// The ContextItemManager supports context layers. A context layer is a
// separation in the set of context items and is useful when providing modal
// functions. For example, when switching modes in the designer to show the
// tab order layout it may be desirable to disable adding items from the
// toolbox and change the user mouse and keyboard gestures to focus on setting
// the tab order. Rather than grabbing and storing context items before
// replacing them with new values, a developer can simply call CreateLayer.
// Once the layer is created, all subsequent context changes go to that layer.
//
// When the developer is done with the layer, as would be the case when a user
// switches out of tab order mode, she simply calls Remove on the layer. This
// removes all context items that were added to the layer and restores the context
// to its previous set of values before the layer was created.
//
[SuppressMessage(FxCop.Category.Naming, FxCop.Rule.IdentifiersShouldHaveCorrectSuffix)]
public abstract class ContextItemManager : IEnumerable
{
//
// Creates a new ContextItemManager object.
//
protected ContextItemManager()
{
}
//
// Returns true if the item manager contains an item of the given type.
//
// The type of item to check.
// True if the context contains an instance of this item type.
// if itemType is null.
public abstract bool Contains(Type itemType);
//
// Returns true if the item manager contains an item of the given type.
//
// The type of item to check.
// True if the context contains an instance of this item type.
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
public bool Contains() where TItemType : ContextItem
{
return Contains(typeof(TItemType));
}
//
// Enumerates the context items in the editing context. This enumeration
// includes prior layers unless the enumerator hits an isolated layer.
// Enumeration is typically not useful in most scenarios but it is provided so
// that developers can search in the context and learn what is placed in it.
//
// An enumeration of context items.
public abstract IEnumerator GetEnumerator();
//
// Returns an instance of the requested item type. If there is no context
// item with the given type, an empty item will be created.
//
// The type of item to return.
// A context item of the requested type. If there is no item in the context of this type a default one will be created.
// if itemType is null.
public abstract ContextItem GetValue(Type itemType);
//
// Returns an instance of the requested item type. If there is no context
// item with the given type, an empty item will be created.
//
// The type of item to return.
// A context item of the requested type. If there is no item in the context of this type a default one will be created.
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
public TItemType GetValue() where TItemType : ContextItem
{
return (TItemType)GetValue(typeof(TItemType));
}
//
// This is a helper method that invokes the protected OnItemChanged
// method on ContextItem.
//
// The editing context in use.
// The new context item.
// The previous context item.
// if context, item or previousItem is null.
protected static void NotifyItemChanged(EditingContext context, ContextItem item, ContextItem previousItem)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
if (item == null)
{
throw FxTrace.Exception.ArgumentNull("item");
}
if (previousItem == null)
{
throw FxTrace.Exception.ArgumentNull("previousItem");
}
item.InvokeOnItemChanged(context, previousItem);
}
//
// This sets a context item to the given value. It is illegal to pass
// null here. If you want to set a context item to its empty value create
// an instance of the item using a default constructor.
//
// The value to set into the context item manager.
public abstract void SetValue(ContextItem value);
//
// Adds an event callback that will be invoked with a context item of the given item type changes.
//
// The type of item you wish to subscribe to.
// A callback that will be invoked when contextItemType changes.
// if contextItemType or callback is null.
public abstract void Subscribe(Type contextItemType, SubscribeContextCallback callback);
//
// Adds an event callback that will be invoked with a context item of the given item type changes.
//
// The type of item you wish to subscribe to.
// A callback that will be invoked when contextItemType changes.
// if callback is null.
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
public void Subscribe(SubscribeContextCallback callback) where TContextItemType : ContextItem
{
if (callback == null)
{
throw FxTrace.Exception.ArgumentNull("callback");
}
SubscribeProxy proxy = new SubscribeProxy(callback);
Subscribe(typeof(TContextItemType), proxy.Callback);
}
//
// Removes a subscription.
//
// The type of context item to remove the callback from.
// The callback to remove.
// if callback is null.
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.GenericMethodsShouldProvideTypeParameter)]
public void Unsubscribe(SubscribeContextCallback callback) where TContextItemType : ContextItem
{
if (callback == null)
{
throw FxTrace.Exception.ArgumentNull("callback");
}
SubscribeProxy proxy = new SubscribeProxy(callback);
Unsubscribe(typeof(TContextItemType), proxy.Callback);
}
//
// Removes a subscription.
//
// The type of context item to remove the callback from.
// The callback to remove.
// if contextItemType or callback is null.
public abstract void Unsubscribe(Type contextItemType, SubscribeContextCallback callback);
//
// This is a helper method that returns the target object for a delegate.
// If the delegate was created to proxy a generic delegate, this will correctly
// return the original object, not the proxy.
//
// The callback whose target you want.
// if callback is null.
// The target object of the callback.
protected static object GetTarget(Delegate callback)
{
if (callback == null)
{
throw FxTrace.Exception.ArgumentNull("callback");
}
ICallbackProxy proxy = callback.Target as ICallbackProxy;
if (proxy != null)
{
return proxy.OriginalTarget;
}
return callback.Target;
}
//
// This is a helper method that performs a Delegate.Remove, but knows
// how to unwrap delegates that are proxies to generic callbacks. Use
// this in your Unsubscribe implementations.
//
// The existing delegate.
// The delegate to be removed from existing.
// The new delegate that should be assigned to existing.
protected static Delegate RemoveCallback(Delegate existing, Delegate toRemove)
{
if (existing == null)
{
return null;
}
if (toRemove == null)
{
return existing;
}
ICallbackProxy toRemoveProxy = toRemove.Target as ICallbackProxy;
if (toRemoveProxy == null)
{
// The item to be removed is a normal delegate. Just call
// Delegate.Remove
return Delegate.Remove(existing, toRemove);
}
toRemove = toRemoveProxy.OriginalDelegate;
Delegate[] invocationList = existing.GetInvocationList();
bool removedItems = false;
for (int idx = 0; idx < invocationList.Length; idx++)
{
Delegate item = invocationList[idx];
ICallbackProxy itemProxy = item.Target as ICallbackProxy;
if (itemProxy != null)
{
item = itemProxy.OriginalDelegate;
}
if (item.Equals(toRemove))
{
invocationList[idx] = null;
removedItems = true;
}
}
if (removedItems)
{
// We must create a new delegate containing the
// invocation list that is is left
existing = null;
foreach (Delegate d in invocationList)
{
if (d != null)
{
if (existing == null)
{
existing = d;
}
else
{
existing = Delegate.Combine(existing, d);
}
}
}
}
return existing;
}
//
// Implementation of default IEnumerable.
//
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private interface ICallbackProxy
{
Delegate OriginalDelegate
{ get; }
object OriginalTarget
{ get; }
}
//
// This is a simple proxy that converts a non-generic subscribe callback to a generic
// one.
//
//
private class SubscribeProxy : ICallbackProxy where TContextItemType : ContextItem
{
private SubscribeContextCallback _genericCallback;
internal SubscribeProxy(SubscribeContextCallback callback)
{
_genericCallback = callback;
}
internal SubscribeContextCallback Callback
{
get {
return new SubscribeContextCallback(SubscribeContext);
}
}
Delegate ICallbackProxy.OriginalDelegate
{
get { return _genericCallback; }
}
object ICallbackProxy.OriginalTarget
{
get {
return _genericCallback.Target;
}
}
private void SubscribeContext(ContextItem item)
{
if (item == null)
{
throw FxTrace.Exception.ArgumentNull("item");
}
_genericCallback((TContextItemType)item);
}
}
}
}