772 lines
24 KiB
C#
772 lines
24 KiB
C#
//------------------------------------------------------------------------------
|
|
/// <copyright from='1997' to='2002' company='Microsoft Corporation'>
|
|
/// Copyright (c) Microsoft Corporation. All Rights Reserved.
|
|
///
|
|
/// This source code is intended only as a supplement to Microsoft
|
|
/// Development Tools and/or on-line documentation. See these other
|
|
/// materials for detailed information regarding Microsoft code samples.
|
|
///
|
|
/// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
namespace SampleDesignerHost
|
|
{
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Design;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Reflection;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Forms;
|
|
|
|
public class EventDescription
|
|
{
|
|
public string EventName;
|
|
public EventDescriptor e;
|
|
|
|
public int line_num = 0;
|
|
public int column_num = 0;
|
|
|
|
public VisualPascalABC.CodeFileDocumentTextEditorControl editor = null;
|
|
|
|
public EventDescription(string event_name, EventDescriptor descriptor)
|
|
{
|
|
EventName = event_name;
|
|
e = descriptor;
|
|
}
|
|
}
|
|
|
|
/// This class provides a default implementation of the event
|
|
/// binding service.
|
|
public class SampleEventBindingService : IEventBindingService
|
|
{
|
|
private Hashtable _eventProperties;
|
|
private IServiceProvider _provider;
|
|
|
|
/// You must provide a service provider to the binding
|
|
/// service. We give it our host.
|
|
public SampleEventBindingService(IServiceProvider provider)
|
|
{
|
|
if (provider == null)
|
|
{
|
|
throw new ArgumentNullException("provider");
|
|
}
|
|
_provider = provider;
|
|
}
|
|
|
|
/// Creates a unique method name. The name must be
|
|
/// compatible with the script language being used and
|
|
/// it must not conflict with any other name in the user's
|
|
/// code. Since we have no code editor, we can guarantee this
|
|
/// method name will be unique. However, if code were editable,
|
|
/// we would have to check our codeCompileUnit's methods.
|
|
protected string CreateUniqueMethodName(IComponent component, EventDescriptor e)
|
|
{
|
|
string name = component.ToString().Split(new char[] { ' ' })[0];
|
|
//return "handler_" + name + "_" + e.Name;
|
|
return name + "_" + e.Name;
|
|
}
|
|
|
|
/// This provides a notification that a particular method
|
|
/// is no longer being used by an event handler. Some implementations
|
|
/// may want to remove the event hander when no events are using
|
|
/// it. By overriding UseMethod and FreeMethod, an implementation
|
|
/// can know when a method is no longer needed.
|
|
protected virtual void FreeMethod(object component, EventDescriptor e, string methodName)
|
|
{
|
|
// UNIMPLEMENTED - We don't add method signatures for our handlers, so
|
|
// don't need to worry about removing them.
|
|
}
|
|
|
|
/// Returns a collection of strings. Each string is
|
|
/// the method name of a method whose signature is
|
|
/// compatible with the delegate contained in the
|
|
/// event descriptor. This should return an empty
|
|
/// collection if no names are compatible.
|
|
protected ICollection GetCompatibleMethods(EventDescriptor e)
|
|
{
|
|
// EMPTY IMPLEMENTATION
|
|
return new string[] {};
|
|
}
|
|
|
|
/// Generates a key based on a method name and it's parameters by just concatenating the
|
|
/// parameters.
|
|
private string GetEventDescriptorHashCode(EventDescriptor eventDesc)
|
|
{
|
|
StringBuilder builder = new StringBuilder(eventDesc.Name);
|
|
builder.Append(eventDesc.EventType.GetHashCode().ToString());
|
|
|
|
foreach(Attribute a in eventDesc.Attributes)
|
|
{
|
|
builder.Append(a.GetHashCode().ToString());
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
/// Gets the requested service from our service provider (the host).
|
|
protected object GetService(Type serviceType)
|
|
{
|
|
if (_provider != null)
|
|
{
|
|
return _provider.GetService(serviceType);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Shows the user code. This method does not show any
|
|
/// particular code; generally it shows the last code the
|
|
/// user typed. This returns true if it was possible to
|
|
/// show the code, or false if not. We are never showing code
|
|
/// since we do not generate handler methods.
|
|
protected bool ShowCode()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// Shows the user code at the given line number. Line
|
|
/// numbers are one-based. This returns true if it was
|
|
/// possible to show the code, or false if not. We are
|
|
/// never showing code since we do not generate handler methods.
|
|
protected bool ShowCode(int lineNumber)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// Shows the body of the user code with the given method
|
|
/// name. This returns true if it was possible to show
|
|
/// the code, or false if not. We are never showing code
|
|
/// since we do not generate handler methods.
|
|
protected bool ShowCode(object component, EventDescriptor e, string methodName)
|
|
{
|
|
EventDescription ev = new EventDescription(methodName, e);
|
|
VisualPascalABC.CodeFileDocumentControl cfdc = VisualPascalABC.Form1.Form1_object._currentCodeFileDocument;
|
|
cfdc.GenerateDesignerCode(ev);
|
|
if (ev.editor != null)
|
|
{
|
|
cfdc.DesignerAndCodeTabs.SelectedTab = cfdc.TextPage;
|
|
VisualPascalABC.VisualPABCSingleton.MainForm.VisualEnvironmentCompiler.ExecuteSourceLocationAction(
|
|
new PascalABCCompiler.SourceLocation(cfdc.FileName, ev.line_num, ev.column_num, ev.line_num, ev.column_num),
|
|
VisualPascalABCPlugins.SourceLocationAction.GotoBeg);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected void ShowMessageBadCode()
|
|
{
|
|
VisualPascalABC.Form1.Form1_object._currentCodeFileDocument.DesignerAndCodeTabs.SelectedTab =
|
|
VisualPascalABC.Form1.Form1_object._currentCodeFileDocument.TextPage;
|
|
MessageBox.Show(PascalABCCompiler.StringResources.Get("VP_MF_CAN_NOT_NAVIGATE_TO_EVENT_HANDLER"),
|
|
PascalABCCompiler.StringResources.Get("VP_MF_FORM_DESIGNER"),
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
|
|
/// This provides a notification that a particular method
|
|
/// is being used by an event handler. Some implementations
|
|
/// may want to remove the event hander when no events are using
|
|
/// it. By overriding UseMethod and FreeMethod, an implementation
|
|
/// can know when a method is no longer needed.
|
|
protected virtual void UseMethod(object component, EventDescriptor e, string methodName)
|
|
{
|
|
// UNIMPLEMENTED - We do not add method signatures to our code.
|
|
}
|
|
|
|
/// This validates that the provided method name is valid for
|
|
/// the language / script being used. The default does nothing.
|
|
/// You may override this and throw an exception if the name
|
|
/// is invalid for your use.
|
|
protected virtual void ValidateMethodName(string methodName)
|
|
{
|
|
// UNIMPLEMENTED - We are guaranteed our method names are valid.
|
|
}
|
|
|
|
/// This creates a name for an event handling method for the given component
|
|
/// and event. The name that is created is guaranteed to be unique in the user's source
|
|
/// code.
|
|
string IEventBindingService.CreateUniqueMethodName(IComponent component, EventDescriptor e)
|
|
{
|
|
if (component == null)
|
|
{
|
|
throw new ArgumentNullException("component");
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
throw new ArgumentNullException("e");
|
|
}
|
|
|
|
return CreateUniqueMethodName(component, e);
|
|
}
|
|
|
|
/// Retrieves a collection of strings. Each string is the name of a method
|
|
/// in user code that has a signature that is compatible with the given event.
|
|
ICollection IEventBindingService.GetCompatibleMethods(EventDescriptor e)
|
|
{
|
|
if (e == null)
|
|
{
|
|
throw new ArgumentNullException("e");
|
|
}
|
|
|
|
return GetCompatibleMethods(e);
|
|
}
|
|
|
|
/// For properties that are representing events, this will return the event
|
|
/// that the property represents.
|
|
EventDescriptor IEventBindingService.GetEvent(PropertyDescriptor property)
|
|
{
|
|
if (property is EventPropertyDescriptor)
|
|
{
|
|
return ((EventPropertyDescriptor)property).Event;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Converts a set of events to a set of properties.
|
|
PropertyDescriptorCollection IEventBindingService.GetEventProperties(EventDescriptorCollection events)
|
|
{
|
|
|
|
if (events == null)
|
|
{
|
|
throw new ArgumentNullException("events");
|
|
}
|
|
|
|
PropertyDescriptor[] props = new PropertyDescriptor[events.Count];
|
|
|
|
// We cache the property descriptors here for speed. Create those for
|
|
// events that we don't have yet.
|
|
//
|
|
if (_eventProperties == null)
|
|
{
|
|
_eventProperties = new Hashtable();
|
|
}
|
|
|
|
for (int i = 0; i < events.Count; i++)
|
|
{
|
|
|
|
object eventHashCode = GetEventDescriptorHashCode(events[i]);
|
|
|
|
props[i] = (PropertyDescriptor)_eventProperties[eventHashCode];
|
|
|
|
if (props[i] == null)
|
|
{
|
|
props[i] = new EventPropertyDescriptor(events[i], this);
|
|
_eventProperties[eventHashCode] = props[i];
|
|
}
|
|
}
|
|
|
|
return new PropertyDescriptorCollection(props);
|
|
}
|
|
|
|
/// Converts a single event to a property.
|
|
PropertyDescriptor IEventBindingService.GetEventProperty(EventDescriptor e)
|
|
{
|
|
|
|
if (e == null)
|
|
{
|
|
throw new ArgumentNullException("e");
|
|
}
|
|
|
|
if (_eventProperties == null)
|
|
{
|
|
_eventProperties = new Hashtable();
|
|
}
|
|
|
|
object eventHashCode = GetEventDescriptorHashCode(e);
|
|
|
|
PropertyDescriptor prop = (PropertyDescriptor)_eventProperties[eventHashCode];
|
|
|
|
if (prop == null)
|
|
{
|
|
prop = new EventPropertyDescriptor(e, this);
|
|
_eventProperties[eventHashCode] = prop;
|
|
}
|
|
|
|
return prop;
|
|
}
|
|
|
|
/// Displays the user code for this designer. This will return true if the user
|
|
/// code could be displayed, or false otherwise.
|
|
bool IEventBindingService.ShowCode()
|
|
{
|
|
return ShowCode();
|
|
}
|
|
|
|
/// Displays the user code for the designer. This will return true if the user
|
|
/// code could be displayed, or false otherwise.
|
|
bool IEventBindingService.ShowCode(int lineNumber)
|
|
{
|
|
return ShowCode(lineNumber);
|
|
}
|
|
|
|
/// Displays the user code for the given event. This will return true if the user
|
|
/// code could be displayed, or false otherwise.
|
|
bool IEventBindingService.ShowCode(IComponent component, EventDescriptor e)
|
|
{
|
|
if (component == null)
|
|
{
|
|
throw new ArgumentNullException("component");
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
throw new ArgumentNullException("e");
|
|
}
|
|
|
|
PropertyDescriptor prop = ((IEventBindingService)this).GetEventProperty(e);
|
|
|
|
string methodName = (string)prop.GetValue(component);
|
|
if (methodName == null)
|
|
{
|
|
return false; // the event is not bound to a method.
|
|
}
|
|
|
|
return ShowCode(component, e, methodName);
|
|
}
|
|
|
|
/// This is an EventDescriptor cleverly wrapped in a PropertyDescriptor
|
|
/// of type String. Note that we now handle subobjects by storing their
|
|
/// event information in their base component's site's dictionary.
|
|
/// Note also that when a value is set for this property we will code-gen
|
|
/// the event method. If the property is set to a new value we will
|
|
/// remove the old event method ONLY if it is empty.
|
|
private class EventPropertyDescriptor : PropertyDescriptor
|
|
{
|
|
private EventDescriptor _eventDesc;
|
|
private SampleEventBindingService _eventSvc;
|
|
private TypeConverter _converter;
|
|
|
|
/// Creates a new EventPropertyDescriptor.
|
|
internal EventPropertyDescriptor(EventDescriptor eventDesc, SampleEventBindingService eventSvc) : base(eventDesc, null)
|
|
{
|
|
_eventDesc = eventDesc;
|
|
_eventSvc = eventSvc;
|
|
}
|
|
|
|
/// Indicates whether reset will change the value of the component. If there
|
|
/// is a DefaultValueAttribute, then this will return true if getValue returns
|
|
/// something different than the default value. If there is a reset method and
|
|
/// a shouldPersist method, this will return what shouldPersist returns.
|
|
/// If there is just a reset method, this always returns true. If none of these
|
|
/// cases apply, this returns false.
|
|
public override bool CanResetValue(object component)
|
|
{
|
|
return GetValue(component) != null;
|
|
}
|
|
|
|
/// Retrieves the type of the component this PropertyDescriptor is bound to.
|
|
public override Type ComponentType
|
|
{
|
|
get
|
|
{
|
|
return _eventDesc.ComponentType;
|
|
}
|
|
}
|
|
|
|
/// Retrieves the type converter for this property.
|
|
public override TypeConverter Converter
|
|
{
|
|
get
|
|
{
|
|
if (_converter == null)
|
|
{
|
|
_converter = new EventConverter(_eventDesc);
|
|
}
|
|
|
|
return _converter;
|
|
}
|
|
}
|
|
|
|
/// Retrieves the event descriptor we are representing.
|
|
internal EventDescriptor Event
|
|
{
|
|
get
|
|
{
|
|
return _eventDesc;
|
|
}
|
|
}
|
|
|
|
/// Indicates whether this property is read only.
|
|
public override bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
return Attributes[typeof(ReadOnlyAttribute)].Equals(ReadOnlyAttribute.Yes);
|
|
}
|
|
}
|
|
|
|
/// Retrieves the type of the property.
|
|
public override Type PropertyType
|
|
{
|
|
get
|
|
{
|
|
return _eventDesc.EventType;
|
|
}
|
|
}
|
|
|
|
/// Retrieves the current value of the property on component,
|
|
/// invoking the getXXX method. An exception in the getXXX
|
|
/// method will pass through.
|
|
public override object GetValue(object component)
|
|
{
|
|
|
|
if (component == null)
|
|
{
|
|
throw new ArgumentNullException("component");
|
|
}
|
|
|
|
// We must locate the sited component, because we store data on the dictionary
|
|
// service for the component.
|
|
//
|
|
ISite site = null;
|
|
|
|
if (component is IComponent)
|
|
{
|
|
site = ((IComponent)component).Site;
|
|
}
|
|
|
|
if (site == null)
|
|
{
|
|
IReferenceService rs = _eventSvc._provider.GetService(typeof(IReferenceService)) as IReferenceService;
|
|
if (rs != null)
|
|
{
|
|
IComponent baseComponent = rs.GetComponent(component);
|
|
if (baseComponent != null)
|
|
{
|
|
site = baseComponent.Site;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (site == null)
|
|
{
|
|
Exception ex = new InvalidOperationException("There is no site for component" + component.ToString() + ".");
|
|
throw ex;
|
|
}
|
|
|
|
IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService));
|
|
if (ds == null)
|
|
{
|
|
Exception ex = new InvalidOperationException("Cannot find IDictionaryService.");
|
|
throw ex;
|
|
}
|
|
|
|
return (string)ds.GetValue(new ReferenceEventClosure(component, this));
|
|
}
|
|
|
|
/// Will reset the default value for this property on the component. If
|
|
/// there was a default value passed in as a DefaultValueAttribute, that
|
|
/// value will be set as the value of the property on the component. If
|
|
/// there was no default value passed in, a ResetXXX method will be looked
|
|
/// for. If one is found, it will be invoked. If one is not found, this
|
|
/// is a nop.
|
|
public override void ResetValue(object component)
|
|
{
|
|
SetValue(component, null);
|
|
}
|
|
|
|
/// This will set value to be the new value of this property on the
|
|
/// component by invoking the setXXX method on the component. If the
|
|
/// value specified is invalid, the component should throw an exception
|
|
/// which will be passed up. The component designer should design the
|
|
/// property so that getXXX following a setXXX should return the value
|
|
/// passed in if no exception was thrown in the setXXX call.
|
|
public override void SetValue(object component, object value)
|
|
{
|
|
// Argument, state checking. Is it ok to set this event?
|
|
//
|
|
if (IsReadOnly)
|
|
{
|
|
Exception ex = new InvalidOperationException("Tried to set a read only event.");
|
|
throw ex;
|
|
}
|
|
|
|
if (value != null && !(value is string))
|
|
{
|
|
Exception ex = new ArgumentException("Cannot set to value " + value.ToString() + ".");
|
|
throw ex;
|
|
}
|
|
|
|
string name = (string)value;
|
|
if (name != null && name.Length == 0)
|
|
{
|
|
name = null;
|
|
}
|
|
|
|
// Obtain the site for the component. Note that this can be a site
|
|
// to a parent component if we can get to the reference service.
|
|
//
|
|
ISite site = null;
|
|
|
|
if (component is IComponent)
|
|
{
|
|
site = ((IComponent)component).Site;
|
|
}
|
|
|
|
if (site == null)
|
|
{
|
|
IReferenceService rs = _eventSvc._provider.GetService(typeof(IReferenceService)) as IReferenceService;
|
|
if (rs != null)
|
|
{
|
|
IComponent baseComponent = rs.GetComponent(component);
|
|
if (baseComponent != null)
|
|
{
|
|
site = baseComponent.Site;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (site == null)
|
|
{
|
|
Exception ex = new InvalidOperationException("There is no site for component " + component.ToString() + ".");
|
|
throw ex;
|
|
}
|
|
|
|
// The dictionary service is where we store the actual event method name.
|
|
//
|
|
IDictionaryService ds = (IDictionaryService)site.GetService(typeof(IDictionaryService));
|
|
if (ds == null)
|
|
{
|
|
Exception ex = new InvalidOperationException("Cannot find IDictionaryService");
|
|
throw ex;
|
|
}
|
|
|
|
// Get the old method name, ensure that they are different, and then continue.
|
|
//
|
|
ReferenceEventClosure key = new ReferenceEventClosure(component, this);
|
|
string oldName = (string)ds.GetValue(key);
|
|
|
|
if (object.ReferenceEquals(oldName, name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (oldName != null && name != null && oldName.Equals(name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Before we continue our work, ensure that the name is
|
|
// actually valid.
|
|
//
|
|
if (name != null)
|
|
{
|
|
_eventSvc.ValidateMethodName(name);
|
|
}
|
|
|
|
// Ok, the names are different. Fire a changing event to make
|
|
// sure it's OK to perform the change.
|
|
//
|
|
IComponentChangeService change = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
|
|
if (change != null)
|
|
{
|
|
try
|
|
{
|
|
change.OnComponentChanging(component, this);
|
|
}
|
|
catch(CheckoutException coEx)
|
|
{
|
|
if (coEx == CheckoutException.Canceled)
|
|
{
|
|
return;
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// Less chance of success of adding a new method name, so
|
|
// don't release the old name until we verify that adding
|
|
// the new one actually succeeded.
|
|
//
|
|
if (name != null)
|
|
{
|
|
_eventSvc.UseMethod(component, _eventDesc, name);
|
|
}
|
|
|
|
if (oldName != null)
|
|
{
|
|
_eventSvc.FreeMethod(component, _eventDesc, oldName);
|
|
}
|
|
|
|
ds.SetValue(key, name);
|
|
|
|
if (change != null)
|
|
{
|
|
change.OnComponentChanged(component, this, oldName, name);
|
|
}
|
|
|
|
OnValueChanged(component, EventArgs.Empty);
|
|
}
|
|
|
|
/// Indicates whether the value of this property needs to be persisted. In
|
|
/// other words, it indicates whether the state of the property is distinct
|
|
/// from when the component is first instantiated. If there is a default
|
|
/// value specified in this PropertyDescriptor, it will be compared against the
|
|
/// property's current value to determine this. If there is't, the
|
|
/// shouldPersistXXX method is looked for and invoked if found. If both
|
|
/// these routes fail, true will be returned.
|
|
///
|
|
/// If this returns false, a tool should not persist this property's value.
|
|
public override bool ShouldSerializeValue(object component)
|
|
{
|
|
return CanResetValue(component);
|
|
}
|
|
|
|
/// Implements a type converter for event objects.
|
|
private class EventConverter : TypeConverter
|
|
{
|
|
|
|
private EventDescriptor _evt;
|
|
|
|
|
|
/// Creates a new EventConverter.
|
|
|
|
internal EventConverter(EventDescriptor evt)
|
|
{
|
|
_evt = evt;
|
|
}
|
|
|
|
|
|
/// Determines if this converter can convert an object in the given source
|
|
/// type to the native type of the converter.
|
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
|
{
|
|
if (sourceType == typeof(string))
|
|
{
|
|
return true;
|
|
}
|
|
return base.CanConvertFrom(context, sourceType);
|
|
}
|
|
|
|
/// Determines if this converter can convert an object to the given destination
|
|
/// type.
|
|
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
|
{
|
|
if (destinationType == typeof(string))
|
|
{
|
|
return true;
|
|
}
|
|
return base.CanConvertTo(context, destinationType);
|
|
}
|
|
|
|
/// Converts the given object to the converter's native type.
|
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return value;
|
|
}
|
|
if (value is string)
|
|
{
|
|
if (((string)value).Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
return value;
|
|
}
|
|
return base.ConvertFrom(context, culture, value);
|
|
}
|
|
|
|
/// Converts the given object to another type. The most common types to convert
|
|
/// are to and from a string object. The default implementation will make a call
|
|
/// to ToString on the object if the object is valid and if the destination
|
|
/// type is string. If this cannot convert to the desitnation type, this will
|
|
/// throw a NotSupportedException.
|
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
|
{
|
|
if (destinationType == typeof(string))
|
|
{
|
|
return value == null ? string.Empty : value;
|
|
}
|
|
return base.ConvertTo(context, culture, value, destinationType);
|
|
}
|
|
|
|
/// Retrieves a collection containing a set of standard values
|
|
/// for the data type this validator is designed for. This
|
|
/// will return null if the data type does not support a
|
|
/// standard set of values.
|
|
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
|
{
|
|
// We cannot cache this because it depends on the contents of the source file.
|
|
//
|
|
string[] eventMethods = null;
|
|
|
|
if (context != null)
|
|
{
|
|
IEventBindingService ebs = (IEventBindingService)context.GetService(typeof(IEventBindingService));
|
|
if (ebs != null)
|
|
{
|
|
ICollection methods = ebs.GetCompatibleMethods(_evt);
|
|
eventMethods = new string[methods.Count];
|
|
int i = 0;
|
|
foreach(string s in methods)
|
|
{
|
|
eventMethods[i++] = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
return new StandardValuesCollection(eventMethods);
|
|
}
|
|
|
|
/// Determines if the list of standard values returned from
|
|
/// GetStandardValues is an exclusive list. If the list
|
|
/// is exclusive, then no other values are valid, such as
|
|
/// in an enum data type. If the list is not exclusive,
|
|
/// then there are other valid values besides the list of
|
|
/// standard values GetStandardValues provides.
|
|
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// Determines if this object supports a standard set of values
|
|
/// that can be picked from a list.
|
|
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// This is a combination of a reference and a property, so that it can be used
|
|
/// as the key of a hashtable. This is because we may have subobjects that share
|
|
/// the same property.
|
|
private class ReferenceEventClosure
|
|
{
|
|
object reference;
|
|
EventPropertyDescriptor propertyDescriptor;
|
|
|
|
public ReferenceEventClosure(object reference, EventPropertyDescriptor prop)
|
|
{
|
|
this.reference = reference;
|
|
this.propertyDescriptor = prop;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return reference.GetHashCode() * propertyDescriptor.GetHashCode();
|
|
}
|
|
|
|
public override bool Equals(Object otherClosure)
|
|
{
|
|
if (otherClosure is ReferenceEventClosure)
|
|
{
|
|
ReferenceEventClosure typedClosure = (ReferenceEventClosure) otherClosure;
|
|
return(typedClosure.reference == reference &&
|
|
typedClosure.propertyDescriptor == propertyDescriptor);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|