Selection Rectangle not moving along with datagrid content during scroll.

Amit 20 Reputation points
2024-04-11T05:08:56.16+00:00

I am attempting to replicate the selection behavior of Windows File Explorer within a WPF DataGrid. Specifically, I aim to implement a selection rectangle that mimics the functionality of File Explorer's selection mechanism. Currently, I have created an adorner rectangle to facilitate the selection of items. However, I encounter an issue when attempting to select rows while scrolling. As the DataGrid's content scrolls within its own ScrollViewer, the rows that are not intersecting with the selection rectangle are being deselected as the Selection rectangle is static and not moving along with the datagrid content during scroll. To provide a clearer understanding of the issue, I have included images below for reference.file

This is my xaml code

<Style x:Key="@DataGridRowStyle" TargetType="DataGridRow">

   <Setter Property="Background" Value="Transparent" />

   <Setter Property="SnapsToDevicePixels" Value="true" />

   <Setter Property="Height" Value="28" />

   <Setter Property="Template">

       <Setter.Value>

           <ControlTemplate TargetType="DataGridRow">

               <Grid Height="28" SnapsToDevicePixels="True">

                   <Border

                       x:Name="Bd"

                       Background="{TemplateBinding Background}"

                       BorderBrush="{TemplateBinding BorderBrush}"

                       BorderThickness="{TemplateBinding BorderThickness}" />

                   <DataGridCellsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" TextBlock.Foreground="{TemplateBinding Foreground}" />

               </Grid>

               <ControlTemplate.Triggers>

                   <Trigger Property="IsMouseOver" Value="true">

                       <Setter TargetName="Bd" Property="Background" Value="transparent" />

                   </Trigger>

                   <!--<Trigger Property="IsSelected" Value="true">

                       <Setter TargetName="Bd" Property="Background" Value="blue" />

                   </Trigger>-->

                   <MultiTrigger>

                       <MultiTrigger.Conditions>

                           <Condition Property="IsSelected" Value="true" />

                           <Condition Property="Selector.IsSelectionActive" Value="false" />

                       </MultiTrigger.Conditions>

                       <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />

                       <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />

                   </MultiTrigger>

                   <Trigger Property="IsEnabled" Value="false">

                       <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />

                   </Trigger>

               </ControlTemplate.Triggers>

           </ControlTemplate>

       </Setter.Value>

   </Setter>
```   </Style>  
<Grid Grid.Row="2" x:Name="FT_grid">

```xml
     <DataGrid

 Grid.Row="2"



 x:Name="FileDataGrid"

 HeadersVisibility="Column"

 HorizontalAlignment="Stretch"

 VerticalAlignment="Stretch"

 AutoGenerateColumns="False"

 CanUserResizeColumns="True"

 GridLinesVisibility="None"

 IsReadOnly="False"

 HorizontalScrollBarVisibility="Auto"

 PreviewMouseLeftButtonDown="DataGrid_PreviewMouseLeftButtonDown"

 PreviewMouseLeftButtonUp="DataGrid_PreviewMouseLeftButtonUp"

 RowHeight="25"

 Loaded="Grid_Loaded"

 PreviewMouseMove="DataGrid_PreviewMouseMove"

ScrollViewer.ScrollChanged="FileDataGrid_ScrollChanged"

 SelectionChanged="FileDataGrid_SelectionChanged"



 SelectionMode="Extended">

         <!--SizeChanged="FileDataGrid_SizeChanged"-->

         <!-- Your DataGrid columns and templates -->

         <!--PreviewMouseMove="DataGrid_PreviewMouseMove"-->

         <DataGrid.CellStyle>

             <Style TargetType="{x:Type DataGridCell}">

                 <Style.Triggers>

                     <Trigger Property="DataGridCell.IsSelected" Value="True">

                         <Setter Property="BorderBrush">

                             <Setter.Value>

                                 <SolidColorBrush Color="Transparent" />

                             </Setter.Value>

                         </Setter>

                         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />

                         <Setter Property="Background">

                             <Setter.Value>

                                 <SolidColorBrush Color="Yellow" />

                             </Setter.Value>

                         </Setter>

                     </Trigger>

                 </Style.Triggers>

             </Style>

         </DataGrid.CellStyle>

         <DataGrid.RowStyle>

             <Style BasedOn="{StaticResource @DataGridRowStyle}" TargetType="DataGridRow">

                 <Setter Property="IsHitTestVisible" Value="True" />

                 <EventSetter Event="MouseEnter" Handler="DataGridRow_MouseEnter"/>

             </Style>

         </DataGrid.RowStyle>

         <DataGrid.Columns>

             <DataGridTemplateColumn

             x:Name="LeftName"


         

               Width="1.5*"

             MinWidth="180"

             CanUserResize="True"

             CanUserSort="True"

             Header="Name"

             IsReadOnly="False"

             SortMemberPath="Name">

                 <DataGridTemplateColumn.CellTemplate>

                     <DataTemplate>

                         <StackPanel Orientation="Horizontal">

                             <Image

                             Width="17"

                             Height="17"

                             Margin="15,0,-25,0"

                             HorizontalAlignment="Right"

                             Source="Images/sms-mobile.png" />

                             <TextBlock

                             Margin="35,4,0,0"

                             Text="{Binding Name}"

                             ToolTip="{Binding Folder}" />

                         </StackPanel>

                     </DataTemplate>

                 </DataGridTemplateColumn.CellTemplate>

                 <DataGridTemplateColumn.CellEditingTemplate>

                     <DataTemplate>

                         <StackPanel Orientation="Horizontal">

                             <Image

                             Width="17"

                             Height="17"

                             Margin="15,0,-25,0"

                             HorizontalAlignment="Right"

                             Source="Images/sms-mobile.png" />

                             <TextBox

                             Width="auto"

                             Height="Auto"

                             Margin="35,0,10,0"

                             Padding="0"

                             HorizontalAlignment="Left"

                             VerticalAlignment="Center"

                             BorderBrush="Blue"

                             BorderThickness="1"

                             Loaded="TextBox_Loaded"

                             Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

                         </StackPanel>

                     </DataTemplate>

                 </DataGridTemplateColumn.CellEditingTemplate>

             </DataGridTemplateColumn>

             <DataGridTextColumn

             x:Name="sizecolumn"

            MaxWidth="100"

             Binding="{Binding FormattedSize}"

             Header="Size" />

             <DataGridTextColumn

             x:Name="lastModifiedColumn"

           MaxWidth="120"

             Binding="{Binding LastModified}"

             Header="Date Modified " />

         </DataGrid.Columns>      

 </DataGrid>  
Code Behind  
  private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

  {

haskell
  isleftbuttonpressed = true;

  isSelecting = true;

  selectionStartPoint = e.GetPosition(FileDataGrid);



  adorner = new SelectionAdorner(FileDataGrid, selectionStartPoint);

  var adornerLayer = AdornerLayer.GetAdornerLayer(FileDataGrid);

  adornerLayer.Add(adorner);

  adorner.SetStartPoint(selectionStartPoint);
  }

private void DataGrid_PreviewMouseMove(object sender, MouseEventArgs e)

{

typescript
if (e.LeftButton == MouseButtonState.Pressed)

{

    isleftbuttonpressed = true;


  

}

if (isSelecting && adorner != null)

{

    adorner.SetEndPoint(e.GetPosition(FileDataGrid));

    var selectionRect = adorner.GetSelectionRect();

    SelectItemsInRectangle(selectionRect, FileDataGrid, sender, e);

}  
 private void SelectItemsInRectangle(Rect selectionRect, DataGrid dataGrid, object sender, MouseEventArgs e)

 {

powershell
 foreach (var item in dataGrid.Items)

 {

     var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(item);

     if (row != null)

     {

         var rowBounds = GetRowBoundsRelativeToDataGrid(row, dataGrid);

         // Check if the row intersects with the selection rectangle

         if (selectionRect.IntersectsWith(rowBounds))

         {

             // Select the item and add it to the collection of selected items

             row.IsSelected = true;

         }

         else

         {

             row.IsSelected = false;

         }

     }

 }
 }

private Rect GetRowBoundsRelativeToDataGrid(DataGridRow row, DataGrid dataGrid)

{

 var rowTransform = row.TransformToAncestor(dataGrid);

 var rowPosition = rowTransform.Transform(new Point(0, 0));

 // Calculate the bounds relative to the DataGrid

 return new Rect(rowPosition.X, rowPosition.Y, row.ActualWidth, row.ActualHeight);

}

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,784 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,015 questions
{count} votes

Accepted answer
  1. gekka 9,666 Reputation points MVP
    2024-04-13T03:39:30.33+00:00
    namespace Gekka.WPF.DataGrid.Sample
    {
        using System;
        using System.Windows;
        using System.Windows.Data;
        using System.Windows.Input;
        using System.Windows.Markup;
        using System.Windows.Controls;
        using System.Linq;
        using System.Windows.Media;
        using System.Collections.Generic;
        using System.Collections;
        using System.ComponentModel;
    
        using System.Windows.Media.Animation;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                Grid grid = new Grid();
                this.Content = grid;
    
                DataGrid dataGrid = new DataGrid() { Margin = new Thickness(20), RowHeaderWidth = 50 };
                for (int i = 1; i <= 10; i++)
                {
                    var column = new DataGridTextColumn() { Header = "Column" + i.ToString(), Width = 100, IsReadOnly = true };
                    if (i == 1)
                    {
                        column.Binding = new Binding(".");
                    }
                    dataGrid.Columns.Add(column);
                }
                grid.Children.Add(dataGrid);
                dataGrid.ItemsSource = Enumerable.Range(1, 1000).ToArray();
    
                // <DataGrid local:SelectionAdorner.EnableSelectionRectangle = "True" />
                SelectionAdorner.SetEnableSelectionRectangle(dataGrid, true);
            }
        }
    
        class SelectionAdorner : System.Windows.Documents.Adorner
        {
            #region Attached Proeprty
    
            public static bool GetEnableSelectionRectangle(DataGrid obj) { return (bool)obj.GetValue(EnableSelectionRectangleProperty); }
            public static void SetEnableSelectionRectangle(DataGrid obj, bool value) { obj.SetValue(EnableSelectionRectangleProperty, value); }
    
            public static readonly DependencyProperty EnableSelectionRectangleProperty =
                DependencyProperty.RegisterAttached
                ("EnableSelectionRectangle"
                , typeof(bool)
                , typeof(SelectionAdorner)
                , new UIPropertyMetadata
                    (default(bool)
                        , new PropertyChangedCallback(OnEnableSelectionRectanglePropertyChanged)));
    
            private static void OnEnableSelectionRectanglePropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
            {
                DataGrid target = dpo as DataGrid;
                if (target != null)
                {
                    if ((bool)e.OldValue)
                    {
                        target.PreviewMouseLeftButtonDown -= Target_PreviewMouseLeftButtonDown;
                    }
                    if ((bool)e.NewValue)
                    {
                        target.PreviewMouseLeftButtonDown += Target_PreviewMouseLeftButtonDown;
                    }
                }
            }
    
            private static void Target_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                DataGrid target = sender as DataGrid;
                SelectionAdorner adorner = new SelectionAdorner(target);
                adorner = new SelectionAdorner(target);
    
                adorner.DG_PreviewMouseLeftButtonDown(sender, e);
            }
    
            #endregion
    
            private bool isSelecting;
            private DataGrid dataGrid;
            private ScrollViewer scrollViewer;
            private int hitRowIndex = -1;
            private Point pHitRow;
            private double offsetX;
            private double offsetY;
            private Point pStart;
            private Point pNow;
    
            public Brush BackgroundBrush { get; set; } = new SolidColorBrush(Color.FromArgb(0x3F, 0, 0x7F, 0xFF));
            public Brush BorderBrush { get; set; } = new SolidColorBrush(Colors.Red);
    
            public SelectionAdorner(DataGrid dg) : base(dg)
            {
                this.dataGrid = dg;
                this.IsHitTestVisible = false;
            }
    
            public void DG_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                if (this.scrollViewer == null)
                {
                    this.scrollViewer = this.dataGrid.Template.FindName("DG_ScrollViewer", this.dataGrid) as ScrollViewer;
                    this.scrollViewer.ScrollChanged += scrollViewer_ScrollChanged;
    
                    //this.scrollViewer.CanContentScroll = false;
                }
    
                if (isSelecting || this.scrollViewer == null)
                {
                    return;
                }
    
                this.offsetX = this.scrollViewer.HorizontalOffset;
                this.offsetY = this.scrollViewer.VerticalOffset;
    
                this.pStart = e.GetPosition(this.dataGrid);
                this.pNow = this.pStart;
    
                var hit = VisualTreeHelper.HitTest(this.dataGrid, this.pStart);
                if (hit == null)
                {
                    return;
                }
    
                var rowcells = FindParent<System.Windows.Controls.Primitives.DataGridCellsPresenter>(hit.VisualHit);
                if (rowcells == null)
                {
                    return;
                }
    
                var hitRow = FindParent<DataGridRow>(rowcells);
                pHitRow = e.GetPosition(hitRow);
    
                this.hitRowIndex = this.dataGrid.ItemContainerGenerator.IndexFromContainer(hitRow);
    
                var layer = System.Windows.Documents.AdornerLayer.GetAdornerLayer(this.dataGrid);
                layer.Add(this);
    
                isSelecting = true;
                this.dataGrid.PreviewMouseLeftButtonUp += DG_PreviewMouseLeftButtonUp;
                this.dataGrid.PreviewMouseMove += DG_PreviewMouseMove;
            }
    
            private T FindParent<T>(DependencyObject d) where T : DependencyObject
            {
                while (d != null)
                {
                    if (d is T t)
                    {
                        return t;
                    }
                    d = VisualTreeHelper.GetParent(d);
                }
                return null;
            }
    
            private void DG_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                isSelecting = false;
                this.dataGrid.PreviewMouseLeftButtonUp -= DG_PreviewMouseLeftButtonUp;
                this.dataGrid.PreviewMouseMove -= DG_PreviewMouseMove;
    
                var layer = System.Windows.Documents.AdornerLayer.GetAdornerLayer(this.dataGrid);
                layer.Remove(this);
                hitRowIndex = -1;
    
                base.OnPreviewMouseLeftButtonUp(e);
            }
    
            private void DG_PreviewMouseMove(object sender, MouseEventArgs e)
            {
                base.OnPreviewMouseMove(e);
    
                this.pNow = e.GetPosition(this.dataGrid);
    
                var hit = VisualTreeHelper.HitTest(this.dataGrid, this.pNow);
                if (hit == null)
                {
                    double x = Math.Min(Math.Max(0, pNow.X), this.dataGrid.ActualWidth);
                    double y = Math.Min(Math.Max(0, pNow.Y), this.dataGrid.ActualHeight);
    
                    pNow = new Point(x, y);
                }
                this.InvalidateVisual();
            }
    
            private void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                if (!isSelecting)
                {
                    return;
                }
    
                var h = e.VerticalChange;
    
                this.InvalidateVisual();
            }
    
            protected override void OnRender(DrawingContext drawingContext)
            {
                if (this.isSelecting)
                {
                    double startX = pStart.X - (this.scrollViewer.HorizontalOffset - this.offsetX);
                    double startY = pStart.Y;
                    if (this.scrollViewer.CanContentScroll)
                    {
                        if (offsetY == this.scrollViewer.VerticalOffset)
                        {
                            startY = pStart.Y;
                        }
                        else if (hitRowIndex < this.scrollViewer.VerticalOffset)
                        {
                            startY = 0;
                        }
                        else
                        {
                            var row = this.dataGrid.ItemContainerGenerator.ContainerFromIndex(hitRowIndex) as DataGridRow;
                            if (row == null)
                            {
                                startY = this.dataGrid.ActualHeight;
                            }
                            else
                            {
                                var p = row.TranslatePoint(pHitRow, this.dataGrid);
                                startY = p.Y;
                            }
                        }
                    }
                    else
                    {
                        startY = pStart.Y - (this.scrollViewer.VerticalOffset - this.offsetY);
                    }
    
                    startX = Math.Min(Math.Max(0, startX), this.dataGrid.ActualWidth);
                    startY = Math.Min(Math.Max(0, startY), this.dataGrid.ActualHeight);
    
                    var pStartFix = new Point(startX, startY);
                    Rect rect = new Rect(pStartFix, pNow);
    
                    //drawingContext.PushClip(new RectangleGeometry(new Rect(0,0,this.dataGrid.ActualWidth, this.dataGrid.ActualHeight)));
                    drawingContext.DrawRectangle(this.BackgroundBrush, new Pen(this.BorderBrush, 1), rect);
                }
                base.OnRender(drawingContext);
            }
        }
    }
    

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.