I have a subclass for DataGrid which supplies some additional functionality and modifies the default values for some of its properties. One thing in particular is I wanted the grid to be read-only by default, so I set IsReadOnly = true
in its constructor.
Recently, I ran into an issue whereby using the grid in a template caused it to be read-only regardless how IsReadOnly
was set in the template. Looking at the dependency property precedence page, I reached the conclusion that overriding metadata for IsReadOnlyProperty
was the correct way to change its default:
IsReadOnlyProperty.OverrideMetadata(typeof(NewDataGrid), new FrameworkPropertyMetadata(true));
While this resolved the issue with control templates, it appears to break mouse double-click command bindings:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}"/>
</DataGrid.InputBindings>
Is there a better way to change IsReadOnly
's default value without either breaking mouse bindings and/or templates?
DoubleClickCommand
itself is a RoutedUICommand
defined in resources like so:
<RoutedUICommand x:Key="DoubleClickCommand"/>
Out of habit, I typically place this in the parent Window
or UserControl
's resource dictionary, but I can move it to the grid's resources as well without any change in behavior.
The command is then attached to the grid's command bindings:
<DataGrid.CommandBindings>
<CommandBinding Command="{StaticResource DoubleClickCommand}" Executed="DoubleClick_Executed" CanExecute="DoubleClick_CanExecute"/>
</DataGrid.CommandBindings>
I doubt that your issue is related to overriding the default value of the DataGrid.IsReadOnly
property. There is no logical relation between a read-only DataGrid
and mouse input events.
Instead it looks like the mouse events are handled by the internal elements of the DataGrid
like DataGridCell
- independent of the control's read-only state. For example double clicking the background of the DataGrid
will work as expected, whereas clicking on a cell (or the content of a cell) won't.
To fix it, I suggest to override the control's Control.OnMouseDoubleClick
and/or Control.OnPreviewMouseDoubleClick
methods.
This should be generally your first approach before registering event handlers. First override the virtual members of the customized or extended control. It will give you greater control and your code looks cleaner too.
This is because the overrides are always invoked even if the override is a virtual event invocator (for example Control.OnPreviewMouseDoubleClick
) and the control decides to not raise the public associated event.
So, instead of registering a mouse gesture, override the inherited event invocators:
public class NewDataGrid : DataGrid
{
static NewDataGrid()
=> DataGrid.IsReadOnlyProperty.OverrideMetadata(typeof(MyDataGrid), new FrameworkPropertyMetadata(true));
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
if (e.LeftButton is MouseButtonState.Pressed)
{
// TODO::Handle left double click mouse input
}
}
protected override void OnPreviewMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnPreviewMouseDoubleClick(e);
if (e.LeftButton is MouseButtonState.Pressed)
{
// TODO::Handle left double click preview mouse input
}
}
}
This is not related to the recommended fix, but a general recommendation in regards to your original implementation: the way you set the MouseBinding.Command
using the StaticResource
markup extension looks odd. The command should be a RoutedCommand
that is usually defined as static
and referenced in XAML using the x:Static
extension:
public class NewDataGrid : DataGrid
{
public static RoutedCommand DoubleClickCommand { get; } = new RoutedUICommand(
"Raise the tunneling and bubbling MouseDoubleClick event",
"DoubleClickCommand",
typeof(NewDataGrid));
public NewDataGrid()
{
var doubleClickCommandBinding = new CommandBinding(DoubleClickCommand, ExecuteDoubleClickCommand, CanExecuteDoubleClickCommand);
this.CommandBindings.Add(doubleClickCommandBinding);
}
private void CanExecuteDoubleClickCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = true;
private void ExecuteDoubleClickCommand(object sender, ExecutedRoutedEventArgs e)
{
var eventArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left);
OnPreviewMouseDoubleClick(eventArgs);
OnMouseDoubleClick(eventArgs);
}
}
<NewDataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{x:Static local:NewDataGrid.DoubleClickCommand}" />
</NewDataGrid.InputBindings>