namespace VideoBrowser.Extensions { using System; using System.Reflection; using System.Windows; using System.Windows.Data; using System.Windows.Input; using System.Windows.Markup; /// /// Defines the . /// [MarkupExtensionReturnType(typeof(Delegate))] public class EventBindingExtension : MarkupExtension { #region Fields /// /// The command binder attached property. /// private static readonly DependencyProperty CommandBinderProperty = DependencyProperty.RegisterAttached(nameof(CommandBinderProperty).Name(), typeof(ICommand), typeof(EventBindingExtension), new PropertyMetadata(null)); /// /// The command parameter binder attached property. /// private static readonly DependencyProperty CommandParameterBinderProperty = DependencyProperty.RegisterAttached(nameof(CommandParameterBinderProperty).Name(), typeof(object), typeof(EventBindingExtension), new PropertyMetadata(null)); /// /// Name of the Command to be invoked when the event fires /// private readonly string _commandName; #endregion Fields #region Constructors /// /// Initializes a new instance of the class. /// public EventBindingExtension() { } /// /// Initializes a new instance of the class. /// /// The command. public EventBindingExtension(string command) { this._commandName = command; } #endregion Constructors #region Properties /// /// Gets or sets the Command. /// public Binding Command { get; set; } /// /// Gets or sets the CommandParameter. /// public Binding CommandParameter { get; set; } /// /// Gets or sets the Element. /// private DependencyObject Element { get; set; } #endregion Properties #region Methods /// /// The GetCommandBinder. /// /// The obj. /// The . public static ICommand GetCommandBinder(DependencyObject obj) { return (ICommand)obj.GetValue(CommandBinderProperty); } /// /// The GetCommandParameterBinder. /// /// The obj. /// The . public static object GetCommandParameterBinder(DependencyObject obj) { return obj.GetValue(CommandParameterBinderProperty); } /// /// The SetCommandBinder. /// /// The obj. /// The value. public static void SetCommandBinder(DependencyObject obj, ICommand value) { obj.SetValue(CommandBinderProperty, value); } /// /// The SetCommandParameterBinder. /// /// The obj. /// The value. public static void SetCommandParameterBinder(DependencyObject obj, object value) { obj.SetValue(CommandParameterBinderProperty, value); } /// /// The ProvideValue. /// /// . /// . public override object ProvideValue(IServiceProvider serviceProvider) { // Retrieve a reference to the InvokeCommand helper method declared below, using reflection MethodInfo invokeCommand = GetType().GetMethod(nameof(InvokeCommand), BindingFlags.Instance | BindingFlags.NonPublic); if (invokeCommand != null) { // Check if the current context is an event or a method call with two parameters var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (target != null) { this.Element = target.TargetObject as DependencyObject; var property = target.TargetProperty; if (property is EventInfo) { // If the context is an event, simply return the helper method as delegate // (this delegate will be invoked when the event fires) var eventHandlerType = (property as EventInfo).EventHandlerType; return invokeCommand.CreateDelegate(eventHandlerType, this); } else if (property is MethodInfo) { // Some events are represented as method calls with 2 parameters: // The first parameter is the control that acts as the event's sender, // the second parameter is the actual event handler var methodParameters = (property as MethodInfo).GetParameters(); if (methodParameters.Length == 2) { var eventHandlerType = methodParameters[1].ParameterType; return invokeCommand.CreateDelegate(eventHandlerType, this); } } } } throw new InvalidOperationException("The EventBinding markup extension is valid only in the context of events."); } /// /// The GetDataContext. /// /// The sender. /// The . private object GetDataContext(object sender) { if (sender is FrameworkContentElement control) { return control.DataContext; } return sender is FrameworkElement element ? element.DataContext : null; } /// /// The InvokeCommand. /// /// . /// . private void InvokeCommand(object sender, EventArgs args) { var dataContext = this.GetDataContext(sender); if (dataContext == null) { throw new NullReferenceException($"DataContext is null."); } object commandParameter = null; if (this.CommandParameter != null) { BindingOperations.SetBinding(this.Element, CommandParameterBinderProperty, this.CommandParameter); commandParameter = this.Element.GetValue(CommandParameterBinderProperty); BindingOperations.ClearBinding(this.Element, CommandParameterBinderProperty); } var executeParameters = (sender, args, commandParameter); if (!string.IsNullOrEmpty(_commandName) && dataContext != null) { // Find control's ViewModel var viewmodel = dataContext; if (viewmodel != null) { // Command must be declared as public property within ViewModel var commandProperty = viewmodel.GetType().GetProperty(_commandName); if (commandProperty != null && commandProperty.GetValue(viewmodel) is ICommand constructorCommand && constructorCommand.CanExecute(args)) { // Execute Command and pass event arguments as parameter constructorCommand.Execute(executeParameters); return; } } } // Get the binding values. BindingOperations.SetBinding(this.Element, CommandBinderProperty, this.Command); var command = this.Element.GetValue(CommandBinderProperty); BindingOperations.ClearBinding(this.Element, CommandBinderProperty); // Execute the command. if ((command is ICommand relayCommand) && relayCommand.CanExecute(commandParameter)) { relayCommand.Execute(executeParameters); } } #endregion Methods } }