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
}
}