| File: System\ComponentModel\Design\EventBindingService.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Reflection; using System.Windows.Forms; namespace System.ComponentModel.Design; /// <summary> /// This class provides a default implementation of the event /// binding service. /// </summary> public abstract partial class EventBindingService : IEventBindingService { private readonly IServiceProvider _provider; private IComponent? _showCodeComponent; private EventDescriptor? _showCodeEventDescriptor; private string? _showCodeMethodName; /// <summary> /// You must provide a service provider to the binding /// service. /// </summary> protected EventBindingService(IServiceProvider provider) { _provider = provider.OrThrowIfNull(); } /// <summary> /// 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. /// </summary> protected abstract string CreateUniqueMethodName(IComponent component, EventDescriptor e); /// <summary> /// 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. /// </summary> protected virtual void FreeMethod(IComponent component, EventDescriptor e, string methodName) { } /// <summary> /// 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. /// </summary> protected abstract ICollection GetCompatibleMethods(EventDescriptor e); /// <summary> /// Gets the requested service from our service provider. /// </summary> protected object? GetService(Type serviceType) => _provider.GetService(serviceType); /// <summary> /// 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. /// </summary> protected abstract bool ShowCode(); /// <summary> /// 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. /// </summary> protected abstract bool ShowCode(int lineNumber); /// <summary> /// 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. /// </summary> protected abstract bool ShowCode(IComponent component, EventDescriptor e, string methodName); /// <summary> /// 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. /// </summary> protected virtual void UseMethod(IComponent component, EventDescriptor e, string methodName) { } /// <summary> /// 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. /// </summary> protected virtual void ValidateMethodName(string methodName) { } /// <summary> /// 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. /// </summary> string IEventBindingService.CreateUniqueMethodName(IComponent component, EventDescriptor e) { ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(e); return CreateUniqueMethodName(component, e); } /// <summary> /// 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. /// </summary> ICollection IEventBindingService.GetCompatibleMethods(EventDescriptor e) { ArgumentNullException.ThrowIfNull(e); return GetCompatibleMethods(e); } /// <summary> /// For properties that are representing events, this will return the event /// that the property represents. /// </summary> EventDescriptor? IEventBindingService.GetEvent(PropertyDescriptor property) { return (property as EventPropertyDescriptor)?.Event; } /// <summary> /// Returns true if the given event has a generic argument or return value in its raise method. /// </summary> private static bool HasGenericArgument([NotNullWhen(true)] EventDescriptor? ed) { if (ed is null || ed.ComponentType is null) { return false; } EventInfo? evInfo = ed.ComponentType.GetEvent(ed.Name); if (evInfo is null || !evInfo.EventHandlerType!.IsGenericType) { return false; } Type[] args = evInfo.EventHandlerType.GetGenericArguments(); if (args is not null && args.Length > 0) { for (int i = 0; i < args.Length; i++) { if (args[i].IsGenericType) { return true; } } } return false; } /// <summary> /// Converts a set of events to a set of properties. /// </summary> PropertyDescriptorCollection IEventBindingService.GetEventProperties(EventDescriptorCollection events) { ArgumentNullException.ThrowIfNull(events); List<PropertyDescriptor> props = new(events.Count); // We cache the property descriptors here for speed. Create those for // events that we don't have yet. for (int i = 0; i < events.Count; i++) { if (HasGenericArgument(events[i])) { continue; } PropertyDescriptor prop = new EventPropertyDescriptor(events[i]!, this); props.Add(prop); } return new PropertyDescriptorCollection([.. props]); } /// <summary> /// Converts a single event to a property. /// </summary> PropertyDescriptor IEventBindingService.GetEventProperty(EventDescriptor e) { ArgumentNullException.ThrowIfNull(e); PropertyDescriptor prop = new EventPropertyDescriptor(e, this); return prop; } /// <summary> /// Displays the user code for this designer. This will return true if the user /// code could be displayed, or false otherwise. /// </summary> bool IEventBindingService.ShowCode() { return ShowCode(); } /// <summary> /// Displays the user code for the designer. This will return true if the user /// code could be displayed, or false otherwise. /// </summary> bool IEventBindingService.ShowCode(int lineNumber) { return ShowCode(lineNumber); } /// <summary> /// Displays the user code for the given event. This will return true if the user /// code could be displayed, or false otherwise. /// </summary> bool IEventBindingService.ShowCode(IComponent component, EventDescriptor e) { ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(e); PropertyDescriptor prop = ((IEventBindingService)this).GetEventProperty(e); string? methodName = (string?)prop.GetValue(component); if (methodName is null) { return false; // the event is not bound to a method. } Debug.Assert(_showCodeComponent is null && _showCodeEventDescriptor is null && _showCodeMethodName is null, "show code already pending"); _showCodeComponent = component; _showCodeEventDescriptor = e; _showCodeMethodName = methodName; Application.Idle += ShowCodeIdle; return true; } /// <summary> /// Displays the user code for the given event. This will return true if the user /// code could be displayed, or false otherwise. /// </summary> private void ShowCodeIdle(object? sender, EventArgs e) { Application.Idle -= ShowCodeIdle; try { ShowCode(_showCodeComponent!, _showCodeEventDescriptor!, _showCodeMethodName!); } finally { _showCodeComponent = null; _showCodeEventDescriptor = null; _showCodeMethodName = null; } } }