Reputation: 18148
I have a DataGrid
on a WPF page and want to prevent the user from selecting cells. As this feature is needed just for testing, I don't want to change everything in code.
After my DataGrid
is filled, I make sure all of its rows are selected. Now I want to make sure that user cannot select/unselect rows.
I tried setting IsEnabled = false
and IsHitTestVisible = "False"
but both of these solutions disable scrollbars.
Is there any way to do this?
Upvotes: 1
Views: 10256
Reputation: 1
To can stop the user to change the selection of a DataGrid and not restrict the interactivity of the controls in the cells, You can use the following solution:
Add PreviewMouseDown to your DataGrid in XAML:
<DataGrid PreviewMouseDown="DataGrid_DisableSelection">
And define DataGrid_DisableSelection in cs:
private void DataGrid_DisableSelection(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
Upvotes: 0
Reputation: 345
You have two choices:
You disable selection in style (in this case you turn off only color in style, but physically SelectedItem or SelectedItems will change). You can easily find out how you can turn off selection style.
You can disable changing selection without changing SelectedItem or SelectedItems (in this case your selection style will not change too).
In WPF i don't like to override standard controls. So, we need a Behavior
:
public class DisableSelectionDataGridBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectOnPreviewMouseLeftButtonDown;
}
private void AssociatedObjectOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var dependencyObject = AssociatedObject.InputHitTest(e.GetPosition(AssociatedObject)) as DependencyObject;
if (dependencyObject == null) return;
var elements = dependencyObject.GetParents().OfType<FrameworkElement>().Where(DataGridCellExtended.GetIsDisableSelection).ToList();
if (!elements.Any()) return;
e.Handled = true;
var args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton, e.StylusDevice);
args.RoutedEvent = UIElement.MouseLeftButtonDownEvent;
args.Source = e.Source;
elements.ForEach(item =>
{
item.RaiseEvent(args);
var children = item.GetChildren<FrameworkElement>();
children.ForEach(child => child.RaiseEvent(args));
});
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectOnPreviewMouseLeftButtonDown;
}
}
Second, you need an Extended
class:
public class DataGridCellExtended
{
public static readonly DependencyProperty IsDisableSelectionProperty = DependencyProperty.RegisterAttached("IsDisableSelection", typeof(Boolean), typeof(DataGridCellExtended));
public static Boolean GetIsDisableSelection(DependencyObject o)
{
return (Boolean)o.GetValue(IsDisableSelectionProperty);
}
public static void SetIsDisableSelection(DependencyObject o, Boolean value)
{
o.SetValue(IsDisableSelectionProperty, value);
}
}
And finally in XAML you need something like this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type items:YourViewModel}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Button Margin="0"
extends:DataGridCellExtended.IsDisableSelection="True">
<Path Data="M5,0L3,2 1,0 0,1 2,3 0,5 1,6 3,4 5,6 6,5 4,3 6,1z"
Fill="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell}}"
Width="12"
Height="12"
Stretch="Uniform"/>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
You can write your logic for extended class.
public static IEnumerable<DependencyObject> GetParents(this DependencyObject element)
{
if (element != null)
{
while (true)
{
var parent = element.GetParent();
var dependencyObject = parent;
element = parent;
if (dependencyObject == null)
{
break;
}
yield return element;
}
yield break;
}
else
{
throw new ArgumentNullException("element");
}
}
private static IEnumerable<DependencyObject> GetChildrenRecursive(this DependencyObject element)
{
if (element != null)
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var dependencyObject = VisualTreeHelper.GetChild(element, i);
yield return dependencyObject;
foreach (var childrenRecursive in dependencyObject.GetChildrenRecursive())
{
yield return childrenRecursive;
}
}
}
else
{
throw new ArgumentNullException("element");
}
}
Upvotes: -1
Reputation: 132548
Why not just set IsHitTestVisible="False"
for your DataGridRow
or DataGridCell
objects only?
That's easy to do using an implicit style in the <DataGrid.Resources>
, and should only disable hit-testing on the rows or cells, which should leave the other areas of the DataGrid
functional, such as the Headers or ScrollBars
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsHitTestVisible" Value="False" />
</Style>
</DataGrid.Resources>
Upvotes: 10