2

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?

g t
  • 7,287
  • 7
  • 50
  • 85
mans
  • 17,104
  • 45
  • 172
  • 321

2 Answers2

10

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>
Rachel
  • 130,264
  • 66
  • 304
  • 490
-1

You have two choices:

  1. 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.

  2. 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");
    }
}
g t
  • 7,287
  • 7
  • 50
  • 85
Smagin Alexey
  • 305
  • 2
  • 6