Binding double click of a row to a command in a Silverlight Data Grid
I want to be able to route the double-click of a grid to a Command. I'm using Rx to simulate the double click but I can't figure out what control to attach the mouse handler to (the mouse event on the e.Row object in DataGrid.RowLoading event doesn't seem to work).
Anyone got any ideas?
Rx code for handling the Double click is as follows:
Observable.FromEvent<MouseButtonEventArgs>(e.Row, "MouseLeftButtonDown").TimeInterval().Subscribe(evt =>
{
if (evt.Interval.Milliseconds <= 300)
{
// Execut开发者_JAVA技巧e command on double click
}
});
I changed this code from handling MouseLeftButtonDown to MouseLeftButtonUp and it works now. The row must have something else handling the button down events.
Observable.FromEvent<MouseButtonEventArgs>(e.Row, "MouseLeftButtonUp").TimeInterval().Subscribe(evt =>
{
if (evt.Interval != TimeSpan.Zero && evt.Interval.TotalMilliseconds <= 300)
{
// Execute command on double click
}
});
Full source code is included below:
CommandBehaviorBase.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
/// <summary>
/// Provides the base implementation of all Behaviors that can be attached to a <see cref="FrameworkElement"/> which trigger a command.
/// </summary>
/// <typeparam name="T">The type of control this behavior can be attached to, must derive from <see cref="FrameworkElement"/>.</typeparam>
public abstract class CommandBehaviorBase<T> : Behavior<T> where T : FrameworkElement
{
#region Constants and Fields
/// <summary>The DependencyProperty backing store for CommandParameter. This enables animation, styling, binding, etc...</summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(CommandBehaviorBase<T>),
new PropertyMetadata(null, OnCommandParameterPropertyChanged));
/// <summary>The DependencyProperty backing store for Command. This enables animation, styling, binding, etc...</summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(CommandBehaviorBase<T>), new PropertyMetadata(null));
#endregion
/// <summary>
/// Gets or sets the command to execute
/// </summary>
public ICommand Command
{
get
{
return (ICommand)this.GetValue(CommandProperty);
}
set
{
this.SetValue(CommandProperty, value);
}
}
/// <summary>
/// Gets or sets the command parameter to execute with.
/// </summary>
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
this.SetValue(CommandParameterProperty, value);
}
}
/// <summary>
/// Gets or sets the command binding path (Hack for SL3).
/// </summary>
/// <remarks>This is a hack to overcome the fact that we cannot
/// bind to the Command dependency property due to a limitation in Silverlight 3.0
/// This shouldn't be necessary as in Silverlight 4.0 <see cref="DependencyObject"/> supports data binding hooray!</remarks>
public string CommandPath { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this mapping is currently enabled.
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Implements the logic that disables the key mapping based on whether the command can be executed.
/// </summary>
/// <summary>
/// Updates the target object's IsEnabled property based on the commands ability to execute.
/// </summary>
public virtual void UpdateEnabledState()
{
if (this.Command == null && !string.IsNullOrEmpty(this.CommandPath))
{
this.Command = this.AssociatedObject.DataContext.GetPropertyPathValue<ICommand>(this.CommandPath, null);
}
if (this.AssociatedObject == null)
{
this.Command = null;
this.CommandParameter = null;
}
else if (this.Command != null)
{
this.IsEnabled = this.Command.CanExecute(this.CommandParameter);
}
}
/// <summary>
/// Executes the command, if it's set, providing the <see cref="CommandParameter"/>
/// </summary>
protected virtual void ExecuteCommand()
{
if (this.Command != null)
{
this.Command.Execute(this.CommandParameter);
}
}
/// <summary>
/// Attaches to the target <see cref="FrameworkElement"/> and sets up the command.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.UpdateEnabledState();
}
/// <summary>
/// Raised when the command parameter changes, re-evaluates whether the Command can execute
/// </summary>
/// <param name="sender">The KeyCommandBehavior that command parameter changed for.</param>
/// <param name="args">The parameter is not used.</param>
private static void OnCommandParameterPropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((CommandBehaviorBase<T>)sender).UpdateEnabledState();
}
}
DoubleClickCommandBehavior.cs
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;
/// <summary>
/// Provides behavior for any clickable control and will execute a command when the control is double clicked.
/// Does not disable the control if the command cannot be executed.
/// </summary>
public class DoubleClickCommandBehavior : CommandBehaviorBase<FrameworkElement>
{
#region Constants and Fields
/// <summary>
/// Stores the observable that subscribes to click events.
/// </summary>
private IDisposable observable;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the DoubleClickCommandBehavior class.
/// </summary>
public DoubleClickCommandBehavior()
{
// Default double-click interval is 220 milliseconds.
this.Interval = 220;
}
#endregion
/// <summary>
/// Gets or sets the double click interval in milliseconds.
/// </summary>
public int Interval
{
get;
set;
}
/// <summary>
/// Subscribes to the MouseLeftButtonUp of the data grid and times the intervals between the events,
/// if the time between clicks is less than the configured interval the command is executed.
/// </summary>
/// <remarks>Originally attached to MouseLeftButtonDown but the events were not firing.</remarks>
protected override void OnAttached()
{
base.OnAttached();
this.observable =
Observable.FromEvent<MouseButtonEventArgs>(this.AssociatedObject, "MouseLeftButtonUp").TimeInterval().
Subscribe(
evt =>
{
if (evt.Interval != TimeSpan.Zero && evt.Interval.TotalMilliseconds <= this.Interval)
{
this.UpdateEnabledState();
this.ExecuteCommand();
}
});
}
/// <summary>
/// Disposes of the observable
/// </summary>
protected override void OnDetaching()
{
if (this.observable != null)
{
this.observable.Dispose();
this.observable = null;
}
base.OnDetaching();
}
}
I'm having similar problems (though not using Rx to handle the double click, instead using a generic DoubleClickTrigger). My specific problem is more related to the fact that I'm not sure how or where to hook up my trigger. I've tried something like the following:
<data:DataGrid.Resources>
<ControlTemplate x:Key="rowTemplate" TargetType="data:DataGridRow">
<data:DataGridRow>
<fxui:Interaction.Triggers>
<fxui:DoubleClickTrigger>
<Interactivity:InvokeCommandAction Command="{Binding Source={StaticResource selectCommand}}" CommandParameter="{Binding}"/>
</fxui:DoubleClickTrigger>
</fxui:Interaction.Triggers>
</data:DataGridRow>
</ControlTemplate>
</data:DataGrid.Resources>
<data:DataGrid.RowStyle>
<Style TargetType="data:DataGridRow">
<Setter Property="Template" Value="{StaticResource rowTemplate}"/>
</Style>
</data:DataGrid.RowStyle>
With no luck.
精彩评论