Skip to content

Instantly share code, notes, and snippets.

@vbfox
Created December 7, 2011 23:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vbfox/1445370 to your computer and use it in GitHub Desktop.
Save vbfox/1445370 to your computer and use it in GitHub Desktop.
CommandBindings from ICommands for WPF MVVM
namespace BlackFox
{
using System;
using System.Windows;
static class Mvvm
{
public static readonly DependencyProperty CommandBindingsProperty = DependencyProperty.RegisterAttached(
"CommandBindings", typeof(MvvmCommandBindingCollection), typeof(Mvvm),
new PropertyMetadata(null, OnCommandBindingsChanged));
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static MvvmCommandBindingCollection GetCommandBindings(UIElement target)
{
if (target == null) throw new ArgumentNullException("target");
return (MvvmCommandBindingCollection)target.GetValue(CommandBindingsProperty);
}
public static void SetCommandBindings(UIElement target, MvvmCommandBindingCollection value)
{
if (target == null) throw new ArgumentNullException("target");
target.SetValue(CommandBindingsProperty, value);
}
private static void OnCommandBindingsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uiDependencyObject = d as UIElement;
if (uiDependencyObject == null) return;
var oldValue = e.OldValue as MvvmCommandBindingCollection;
if (oldValue != null)
{
oldValue.DettachFrom(uiDependencyObject);
}
var newValue = e.NewValue as MvvmCommandBindingCollection;
if (newValue != null)
{
newValue.AttachTo(uiDependencyObject);
}
}
}
}
namespace BlackFox
{
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
class MvvmCommandBinding : Freezable
{
UIElement uiElement;
readonly CommandBinding commandBinding;
public MvvmCommandBinding()
{
commandBinding = new CommandBinding();
commandBinding.CanExecute += OnCanExecute;
commandBinding.Executed += OnExecute;
}
#region Command
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(MvvmCommandBinding),
new PropertyMetadata(null, OnCommandChanged));
static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MvvmCommandBinding)d).OnCommandChanged((ICommand)e.NewValue);
}
void OnCommandChanged(ICommand newValue)
{
commandBinding.Command = newValue;
}
[Bindable(true)]
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#endregion
#region Target
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
"Target", typeof(ICommand), typeof(MvvmCommandBinding),
new PropertyMetadata(null, OnTargetChanged));
[Bindable(true)]
public ICommand Target
{
get { return (ICommand)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MvvmCommandBinding)d).OnTargetChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
}
void OnTargetChanged(ICommand oldValue, ICommand newValue)
{
if (oldValue != null)
{
oldValue.CanExecuteChanged -= OnTargetCanExecuteChanged;
}
if (newValue != null)
{
newValue.CanExecuteChanged += OnTargetCanExecuteChanged;
}
CommandManager.InvalidateRequerySuggested();
}
#endregion
#region CanExecuteChangedSuggestRequery
public static readonly DependencyProperty CanExecuteChangedSuggestRequeryProperty
= DependencyProperty.Register(
"CanExecuteChangedSuggestRequery", typeof(bool), typeof(MvvmCommandBinding),
new PropertyMetadata(false, OnCanExecuteChangedSuggestRequeryChanged));
[Bindable(true)]
public bool CanExecuteChangedSuggestRequery
{
get { return (bool)GetValue(CanExecuteChangedSuggestRequeryProperty); }
set { SetValue(CanExecuteChangedSuggestRequeryProperty, value); }
}
static void OnCanExecuteChangedSuggestRequeryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MvvmCommandBinding)d).OnCanExecuteChangedSuggestRequeryChanged((bool)e.NewValue);
}
void OnCanExecuteChangedSuggestRequeryChanged(bool newValue)
{
if (newValue)
{
CommandManager.InvalidateRequerySuggested();
}
}
#endregion
#region On event
void OnTargetCanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChangedSuggestRequery)
{
CommandManager.InvalidateRequerySuggested();
}
}
void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
if (Target == null) return;
e.Handled = true;
Target.Execute(e.Parameter);
}
void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (Target == null) return;
e.Handled = true;
e.CanExecute = false;
e.CanExecute = Target.CanExecute(e.Parameter);
}
#endregion
#region Attach / Dettach
internal void DettachFrom(UIElement uiDependencyObject)
{
if (uiDependencyObject == null) throw new ArgumentNullException("uiDependencyObject");
WritePreamble();
if (uiDependencyObject != uiElement) return;
Dettach();
}
void Dettach()
{
uiElement.CommandBindings.Remove(commandBinding);
uiElement = null;
}
internal void AttachTo(UIElement uiDependencyObject)
{
if (uiDependencyObject == null) throw new ArgumentNullException("uiDependencyObject");
WritePreamble();
if (uiElement != null)
{
Dettach();
}
uiElement = uiDependencyObject;
uiDependencyObject.CommandBindings.Add(commandBinding);
}
#endregion
protected override Freezable CreateInstanceCore()
{
return new MvvmCommandBinding();
}
}
}
namespace BlackFox
{
using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
/// <summary>
/// Set an element <see cref="UIElement.CommandBindings"/> with a syntax allowing to specify an
/// <see cref="ICommand"/> instance using bindings if required.
/// </summary>
/// <example>
/// &lt;u:Mvvm.CommandBindings&gt;
/// &lt;u:MvvmCommandBindingCollection&gt;
/// &lt;u:MvvmCommandBinding Command="cmd:RoutedCommands.SomeCommand"
/// Target="{Binding CommandInViewModel}" /&gt;
/// &lt;/u:MvvmCommandBindingCollection&gt;
/// &lt;/u:Mvvm.CommandBindings&gt;
/// </example>
[ContentProperty("Commands")]
class MvvmCommandBindingCollection : Freezable
{
// Normally the inheritance context only goes to the logical and visual tree. But there are some additional
// "Pointers" that exists to simplify XAML programming. The one that we use there is that the context is
// propagated when a hierarchy of Freezable is inside a FrameworkElement.
//
// It is acheived by the facts that :
// * This class is Freezable
// * The collection property is a dependency property
// * The collection is Freezable (FreezableCollection<T> is an Animatable that is a Freezable)
// * The objects inside the collection are instances of Freezable
static readonly DependencyPropertyKey commandsPropertyReadWrite =
DependencyProperty.RegisterReadOnly("Commands", typeof(FreezableCollection<MvvmCommandBinding>),
typeof(MvvmCommandBindingCollection), null);
public static readonly DependencyProperty CommandsProperty = commandsPropertyReadWrite.DependencyProperty;
public FreezableCollection<MvvmCommandBinding> Commands
{
get { return (FreezableCollection<MvvmCommandBinding>)GetValue(CommandsProperty);; }
private set { SetValue(commandsPropertyReadWrite, value); }
}
UIElement uiElement;
public MvvmCommandBindingCollection()
{
Commands = new FreezableCollection<MvvmCommandBinding>();
((INotifyCollectionChanged)Commands).CollectionChanged += CommandsChanged;
}
void CommandsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (uiElement == null) return;
if (e.Action == NotifyCollectionChangedAction.Add
|| e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (MvvmCommandBinding command in e.NewItems)
{
command.AttachTo(uiElement);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove
|| e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (MvvmCommandBinding command in e.OldItems)
{
command.DettachFrom(uiElement);
}
}
}
internal void DettachFrom(UIElement uiDependencyObject)
{
if (uiDependencyObject == null) throw new ArgumentNullException("uiDependencyObject");
WritePreamble();
if (uiDependencyObject != uiElement) return;
Dettach();
}
void Dettach()
{
foreach (var command in Commands)
{
command.DettachFrom(uiElement);
}
uiElement = null;
}
internal void AttachTo(UIElement uiDependencyObject)
{
if (uiDependencyObject == null) throw new ArgumentNullException("uiDependencyObject");
WritePreamble();
if (uiElement != null)
{
Dettach();
}
uiElement = uiDependencyObject;
foreach (var command in Commands)
{
command.AttachTo(uiElement);
}
}
protected override Freezable CreateInstanceCore()
{
return new MvvmCommandBindingCollection();
}
}
}
<Window x:Class="BlackFox.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:f="clr-namespace:BlackFox"
>
<f:Mvvm.CommandBindings>
<f:MvvmCommandBindingCollection>
<f:MvvmCommandBinding Command="f:MyRoutedCommands.SomeRoutedCommand"
Target="{Binding MyCommandInViewModel}"
CanExecuteChangedSuggestRequery="True" />
</f:MvvmCommandBindingCollection>
</f:Mvvm.CommandBindings>
</Window>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment