Today, another example of the powerful ramora pattern: execute a RoutedCommand
on an Control when you click on a button outside of the aimed control scope.
When is it useful? For example, when you use the infragistics grid (xamdatagrid
) and you want to execute the command which removes the selected row from a button outside of the grid. You then have to execute the DeleteSelectedDataRecords
command on the grid from a button which is outside of the scope of the grid ... then this behavior is useful !
Our implementation will also show how we can apply the "Weak Events pattern" with the dependency property trick (attached property / ramora pattern).
And of course, this example can be adapted to subscribe on any event you want and not only the click of a button.
The Behavior Itself
Our behavior will listen to the click button, and the handling of the click event will execute the routed command on the target element.
public class ExecuteCommandOnControl : DependencyObject
{
#region Command
public static readonly DependencyProperty RoutedCommandProperty =
DependencyProperty.RegisterAttached("RoutedCommand", typeof(RoutedCommand)
, typeof(ExecuteCommandOnControl));
public static RoutedCommand GetRoutedCommand(DependencyObject d)
{
return (RoutedCommand)d.GetValue(RoutedCommandProperty);
}
public static void SetRoutedCommand(DependencyObject d, RoutedCommand value)
{
d.SetValue(RoutedCommandProperty, value);
}
#endregion
#region IInputElement
public static readonly DependencyProperty TargetProperty =
DependencyProperty.RegisterAttached("Target", typeof(IInputElement),
typeof(ExecuteCommandOnControl),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnTargetChanged)));
public static IInputElement GetTarget(DependencyObject d)
{
return (IInputElement)d.GetValue(TargetProperty);
}
public static void SetTarget(DependencyObject d, IInputElement value)
{
d.SetValue(TargetProperty, value);
}
private static void OnTargetChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Button button = d as Button;
if (button != null)
{
ButtonClickEventManager.AddListener(button, _customEventListener);
} else
{
throw
new ArgumentException
("This behavior can only be installed on non-null Button !");
}
}
#endregion
private static CustomEventListener _customEventListener = new CustomEventListener();
}
WeakEvents
So what is the class CustomEventListener
? This is the object which will handle the click on the button. This class implements the interface
IWeakEventListener
which is a necessary thing to use the Weak
event pattern.
We will then not subscribe to the click event in the usual way (using the operator +=
) but by calling a WeakEventManager
of our own: ButtonClickEventManager
.
This manager will then keep a weak reference of our listener and the button on which we operate will then be free to be garbage collected. Otherwise, every button on which we subscribe would be kept in memory because each one would be connected to our behavior...
Here is the CustomEventListener
class:
public class CustomEventListener : IWeakEventListener
{
#region IWeakEventListener Members
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(ButtonClickEventManager))
{
var target =
ExecuteCommandOnControl.GetTarget(sender as DependencyObject);
var command =
ExecuteCommandOnControl.GetRoutedCommand(sender as DependencyObject);
if (target == null || command == null) return true;
command.Execute(null, target);
return true;
} else
{
return false;
}
}
#endregion
}
And here is our WeakEventManager
:
public class ButtonClickEventManager : WeakEventManager
{
public static void AddListener(Button source,
IWeakEventListener listener)
{
ButtonClickEventManager.
CurrentManager.ProtectedAddListener(source, listener);
}
public static void RemoveListener(Button source,
IWeakEventListener listener)
{
ButtonClickEventManager.
CurrentManager.ProtectedRemoveListener(source, listener);
}
protected override void StartListening(object source)
{
Button button = (Button)source;
button.Click += this.OnButtonClick;
}
protected override void StopListening(object source)
{
Button button = (Button)source;
button.Click -= this.OnButtonClick;
}
private void OnButtonClick(Object sender, RoutedEventArgs args)
{
base.DeliverEvent(sender, args);
}
private static ButtonClickEventManager CurrentManager
{
get
{
Type managerType = typeof(ButtonClickEventManager);
ButtonClickEventManager manager =
(ButtonClickEventManager)WeakEventManager.GetCurrentManager(managerType);
if (manager == null)
{
manager = new ButtonClickEventManager();
WeakEventManager.SetCurrentManager(managerType, manager);
}
return manager;
}
}
}
Example of Use
Here is a little example of how you can use it to launch the deleteSelected
command of the infragistics datagrid
from a button outside of the grid:
<Button Content="Delete selected"
tools:ExecuteCommandOnControl.RoutedCommand=
"{x:Static igDP:DataPresenterCommands.DeleteSelectedDataRecords}"
tools:ExecuteCommandOnControl.Target="{Binding ElementName=dataGrid}" />
<igDP:XamDataGrid Name="dataGrid" DataSource="{Binding Data}" />
Interesting Readings
CodeProject