using System;
using System.Windows;
using System.Windows.Input;
namespace BuzzGUI.Common
{
///
/// A CommandBinding subclass that will attach its
/// CanExecute and Executed events to the event handling
/// methods on the object referenced by its CommandSink property.
/// Set the attached CommandSink property on the element
/// whose CommandBindings collection contain CommandSinkBindings.
/// If you dynamically create an instance of this class and add it
/// to the CommandBindings of an element, you must explicitly set
/// its CommandSink property.
///
public class CommandSinkBinding : CommandBinding
{
#region CommandSink [instance property]
ICommandSink _commandSink;
public ICommandSink CommandSink
{
get { return _commandSink; }
set
{
if (value == null)
throw new ArgumentNullException("Cannot set CommandSink to null.");
if (_commandSink != null)
throw new InvalidOperationException("Cannot set CommandSink more than once.");
_commandSink = value;
base.CanExecute += (s, e) =>
{
bool handled;
e.CanExecute = _commandSink.CanExecuteCommand(e.Command, e.Parameter, out handled);
e.Handled = handled;
};
base.Executed += (s, e) =>
{
bool handled;
_commandSink.ExecuteCommand(e.Command, e.Parameter, out handled);
e.Handled = handled;
};
}
}
#endregion // CommandSink [instance property]
#region CommandSink [attached property]
public static ICommandSink GetCommandSink(DependencyObject obj)
{
return (ICommandSink)obj.GetValue(CommandSinkProperty);
}
public static void SetCommandSink(DependencyObject obj, ICommandSink value)
{
obj.SetValue(CommandSinkProperty, value);
}
public static readonly DependencyProperty CommandSinkProperty =
DependencyProperty.RegisterAttached(
"CommandSink",
typeof(ICommandSink),
typeof(CommandSinkBinding),
new UIPropertyMetadata(null, OnCommandSinkChanged));
static void OnCommandSinkChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
ICommandSink commandSink = e.NewValue as ICommandSink;
if (!ConfigureDelayedProcessing(depObj, commandSink))
ProcessCommandSinkChanged(depObj, commandSink);
}
// This method is necessary when the CommandSink attached property is set on an element
// in a template, or any other situation in which the element's CommandBindings have not
// yet had a chance to be created and added to its CommandBindings collection.
static bool ConfigureDelayedProcessing(DependencyObject depObj, ICommandSink commandSink)
{
bool isDelayed = false;
CommonElement elem = new CommonElement(depObj);
if (elem.IsValid && !elem.IsLoaded)
{
RoutedEventHandler handler = null;
handler = delegate
{
elem.Loaded -= handler;
ProcessCommandSinkChanged(depObj, commandSink);
};
elem.Loaded += handler;
isDelayed = true;
}
return isDelayed;
}
static void ProcessCommandSinkChanged(DependencyObject depObj, ICommandSink commandSink)
{
CommandBindingCollection cmdBindings = GetCommandBindings(depObj);
if (cmdBindings == null)
throw new ArgumentException("The CommandSinkBinding.CommandSink attached property was set on an element that does not support CommandBindings.");
foreach (CommandBinding cmdBinding in cmdBindings)
{
CommandSinkBinding csb = cmdBinding as CommandSinkBinding;
if (csb != null && csb.CommandSink == null)
csb.CommandSink = commandSink;
}
}
static CommandBindingCollection GetCommandBindings(DependencyObject depObj)
{
var elem = new CommonElement(depObj);
return elem.IsValid ? elem.CommandBindings : null;
}
#endregion // CommandSink [attached property]
#region CommonElement [nested class]
///
/// This class makes it easier to write code that works
/// with the common members of both the FrameworkElement
/// and FrameworkContentElement classes.
///
private class CommonElement
{
readonly FrameworkElement _fe;
readonly FrameworkContentElement _fce;
public readonly bool IsValid;
public CommonElement(DependencyObject depObj)
{
_fe = depObj as FrameworkElement;
_fce = depObj as FrameworkContentElement;
IsValid = _fe != null || _fce != null;
}
public CommandBindingCollection CommandBindings
{
get
{
this.Verify();
if (_fe != null)
return _fe.CommandBindings;
else
return _fce.CommandBindings;
}
}
public bool IsLoaded
{
get
{
this.Verify();
if (_fe != null)
return _fe.IsLoaded;
else
return _fce.IsLoaded;
}
}
public event RoutedEventHandler Loaded
{
add
{
this.Verify();
if (_fe != null)
_fe.Loaded += value;
else
_fce.Loaded += value;
}
remove
{
this.Verify();
if (_fe != null)
_fe.Loaded -= value;
else
_fce.Loaded -= value;
}
}
void Verify()
{
if (!this.IsValid)
throw new InvalidOperationException("Cannot use an invalid CommmonElement.");
}
}
#endregion // CommonElement [nested class]
}
}