|
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Forms;
using System.Windows.Forms.Design;
// RightToCopy & PublishAndPerish: OrlandoCurioso 2011
namespace DynamicEvents.Design
{
/// <summary>
/// Designer for <see cref="ButtonContainer"/> controls.
/// Supports seperate 'ButtonClick' events for individual items.
/// </summary>
internal class ButtonContainerDesigner : ControlDesigner, IServiceProvider
{
#region Fields
private static readonly Type Event_HandlerType = Constants.Event_HandlerType;
private const string Event_DummyDesign = Constants.Event_DummyDesign;
private const string formatDescription = Constants.DESCR_DesignEvent;
private CustomEventDescriptor interceptedEventDescriptor;
private bool reEntrantCodeCancel;
private bool reEntrantCodeSet;
#endregion
#region Initialize & Dispose
public override void Initialize(IComponent component)
{
base.Initialize(component);
attachEventhandler();
}
protected override void Dispose(bool disposing)
{
detachEventhandler();
base.Dispose(disposing);
}
private void attachEventhandler()
{
IComponentChangeService ccs = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (ccs != null)
{
ccs.ComponentChanging += new ComponentChangingEventHandler(ccs_ComponentChanging);
}
Debug.Assert(ccs != null);
}
private void detachEventhandler()
{
IComponentChangeService ccs = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (ccs != null)
{
ccs.ComponentChanging -= new ComponentChangingEventHandler(ccs_ComponentChanging);
}
}
#endregion
#region Generated events
#pragma warning disable 67
/// <summary>
/// Dummy event at design time.
/// Name == <see cref="Event_DummyDesign"/>; Type == <see cref="Event_HandlerType"/>
/// </summary>
public event EventHandler ButtonClick_;
#pragma warning restore 67
private IEnumerable<EnumMemberAttribute> createAttributes()
{
ButtonContainer control = (ButtonContainer)Control;
List<EnumMemberAttribute> attributes = new List<EnumMemberAttribute>();
foreach (string name in Enum.GetNames(control.EnumType))
{
EnumMemberAttribute attr = new EnumMemberAttribute(control.EnumType, Enum.Parse(control.EnumType, name), name);
attributes.Add(attr);
}
return attributes;
}
protected override void PreFilterEvents(IDictionary events)
{
//base.PreFilterEvents(events);
Type componentType = GetType();
List<CustomEventDescriptor> generatedEventDescriptors = new List<CustomEventDescriptor>();
foreach (EnumMemberAttribute enumMemberAttribute in createAttributes())
{
string enumName = enumMemberAttribute.EnumName;
EventDescriptor ed = TypeDescriptor.CreateEvent(componentType, Event_DummyDesign, Event_HandlerType,
new DesignOnlyAttribute(true),
new DisplayNameAttribute(Event_DummyDesign + enumName),
new DescriptionAttribute(string.Format(CultureInfo.CurrentCulture, formatDescription, enumName)),
new MergablePropertyAttribute(false),
enumMemberAttribute);
// must use proxy EventDescriptor. Using TypeAlias with TypeDescriptor.CreateEvent() does not seem to work !?!
CustomEventDescriptor ced = new CustomEventDescriptor(ed, enumMemberAttribute, null);
generatedEventDescriptors.Add(ced);
events.Add(Event_DummyDesign + enumName, ced);
}
EventPropertyTypeConverter.ExchangeTypeConverter(this, generatedEventDescriptors);
}
void ccs_ComponentChanging(object sender, ComponentChangingEventArgs e)
{
if (e.Component != Component || e.Member == null)
{
return;
}
if (reEntrantCodeSet)
{
// e.Member is either interceptedEventDescriptor or it's associated EventPropertyDescriptor
Debug.Assert(e.Member.Attributes[typeof(EnumMemberAttribute)] == interceptedEventDescriptor.Attributes[typeof(EnumMemberAttribute)]);
return;
}
if (reEntrantCodeCancel)
{
// e.Member is never interceptedEventDescriptor or it's associated EventPropertyDescriptor
Debug.Assert(e.Member.Attributes[typeof(EnumMemberAttribute)] != interceptedEventDescriptor.Attributes[typeof(EnumMemberAttribute)]);
// reEntrantCodeCancel: {e.Member.GetType().FullName}
// Cancel triggered Undo operation for a previous intercepted EventPropertyDescriptor or other descriptor
throw CheckoutException.Canceled;
}
CustomEventDescriptor ced = e.Member as CustomEventDescriptor;
if (ced == null)
{
return;
}
//-- EventPropertyDescriptor is setting value
Debug.Assert(interceptedEventDescriptor == null);
interceptedEventDescriptor = ced;
// -- process EventDescriptor, after EventPropertyDescriptor silently cancelled DesignerTransaction, causing new ComponentChanging event
reEntrantCodeCancel = true;
Application.Idle += new EventHandler(Application_Idle);
throw CheckoutException.Canceled;
}
void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
reEntrantCodeCancel = false;
reEntrantCodeSet = true;
Debug.Assert(interceptedEventDescriptor != null);
Debug.Assert(((IDesignerHost)GetService(typeof(IDesignerHost))).InTransaction == false);
try
{
IEventBindingService svc = (IEventBindingService)GetService(typeof(IEventBindingService));
PropertyDescriptor eventProperty = svc.GetEventProperty(interceptedEventDescriptor);
EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)interceptedEventDescriptor.Attributes[typeof(EnumMemberAttribute)];
object curValue = eventProperty.GetValue(Component);
if (curValue == null)
{
// -- create new listener or attach existing having identical name
string methodName = svc.CreateUniqueMethodName(Component, interceptedEventDescriptor);
methodName += enumMemberAttribute.EnumName;
// As the listener name is always created here, despite any value specified by user, only our listener name is ever used -> no compatible methods
// name conflicts are unlikely
// CanDo: validate methodName via CodeTypeDeclaration (obtainable as service)
eventProperty.SetValue(Component, methodName);
// IEventBindingService creates correct listener method and incorrect CodeAttachEventStatement for the dummy event.
// DesignEventSerializer will exchange the CodeAttachEventStatement for 'component.AddEventHandler' invocation, while serializing component.
}
else
{
// -- always reset existing listener
eventProperty.SetValue(Component, null);
// IEventBindingService removes existing listener method, if otherwise unattached.
// DesignEventSerializer will create no 'component.AddEventHandler' invocation, as there is no more CodeAttachEventStatement.
}
}
finally
{
interceptedEventDescriptor = null;
reEntrantCodeSet = false;
}
}
#endregion
#region IServiceProvider Members
object IServiceProvider.GetService(Type serviceType)
{
return GetService(serviceType);
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.