Click here to Skip to main content
15,904,926 members
Please Sign up or sign in to vote.
1.00/5 (3 votes)
See more:
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.

What I have tried:

C#
<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">
                
                        
                
                         <datagrid.cellstyle>
                
                             
                
                                 <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>
                
                             
                
                         
                
                         <datagrid.rowstyle>
                
                             
                
                                 <Setter Property="IsHitTestVisible" Value="True" />
                
                                 <EventSetter Event="MouseEnter" Handler="DataGridRow_MouseEnter"/>
                
                             
                
                         
                
                         <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}">
                
                                         
                
                                     
                
                                 
                
                                 <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}">
                
                                         
                
                                     
                
                                 
                
                             
                
                             <datagridtextcolumn
 
="" x:name="sizecolumn" maxwidth="100" binding="{Binding FormattedSize}" header="Size">
                
                             <datagridtextcolumn
 
="" x:name="lastModifiedColumn" maxwidth="120" binding="{Binding LastModified}" header="Date Modified ">
                
                               
                
                  
            
            private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            
            {
            
            
            
              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)
        
        {
        
        
        
        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)
        
        {
        
        
        
         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;
        
                 }
        
             }
        
         }
        } 

I tried various solution but nothing worked for me.
Posted
Updated 10-Apr-24 19:48pm
v3
Comments
Jo_vb.net 10-Apr-24 9:47am    
please show your xaml and c# code.
use the "Improve question" button:
You can edit and improve this question if it needs clarification
Jo_vb.net 13-Apr-24 12:07pm    
Are you still with us?
Or are you going the way given at the other website?

From what I could make out of your code is that your 'SelectItemsInRectangle' method is only considering the visible rows in your DataGrid at the time that you make the selection. When you scroll, new rows become visible, and your selection rectangle does not account for them.

You can modify your 'SelectItemsInRectangle' method to consider all items in the DataGrid, regardless of their visibility, I have not tested the code but it should point you in the right direction -

C#
private void SelectItemsInRectangle(Rect selectionRect, DataGrid dataGrid, object sender, MouseEventArgs e)
{
    //First, clear your current selection...
    dataGrid.SelectedItems.Clear();

    //Now iterate over all items in your DataGrid...
    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))
            {
                //You can now select the item and add it to your collection of selected items...
                row.IsSelected = true;
                dataGrid.SelectedItems.Add(item);
            }
        }
    }
}
 
Share this answer
 
Comments
Jo_vb.net 13-Apr-24 8:44am    
Looks good.

The only problem seems to me when dragging the mouse form start point at bottom of the grid and the (mouse) endpoint on top is on a column header.

The rectangle then ends on the col header and the selection misses the invisible rows from the bottom.

A workaround would be changing the scrollViewer.VerticalOffset
I think you have to calculate the rectangle position every time when it should move.

Perhaps it is simpler to delete the rectangle which is on wrong position and create a new one on calculated position if needed.

https://stackoverflow.com/questions/12763441/how-to-draw-a-rectangle-in-wpf-at-a-specific-x-y-screen-location[^]

The Win Explorer may not use a rectangle but only change the background of the rows what you could also try for your DataGrid.
 
Share this answer
 
v2
Comments
Member 16239100 11-Apr-24 5:31am    
Hi, @Jo_vb.net i think you did not understand the problem. The problem is the selection rectangle is not the part of the datagrid, its an adorner which is selecting the datagrid rows by hovering over it like we do in windows file explorer. Son here what is happening, the rectangle will remain fixed at one position during scroll as its not the part of the datagrid scrollviewer and the datagrid rows content will scroll up and down. You can see the visual representation here
https://stackoverflow.com/questions/78304588/selection-rectangle-not-moving-along-with-wpf-datagrid-content-during-scroll
You can see in the pic that selection rectangle is above the rows and its like its in the air and the content below it will scroll up and down and it will remain fixed. But in windows file explorer if you see, its the part of datagrid and it will scroll with datagrid content. So is there any way i can fix this problem.
Jo_vb.net 11-Apr-24 5:43am    
Because the rectangle will not move automatically I wrote "calculate the rectangle position every time when it should move".

The Win Explorer may not use a rectangle but only change the background of the rows what you could also try for your DataGrid.
Member 16239100 11-Apr-24 6:01am    
No actually if you see win explorer use a selection rectangle to select the files and folders.
Jo_vb.net 11-Apr-24 6:35am    
For the calculation of one corner of the rectangle you have mouse position when selection starts.

The other, diagonal position is the current mouse position when you drag with the mouse.

Then you can calculate the current rectangle.
Member 16239100 11-Apr-24 7:54am    
so on mouse move i just need to update the end point or do i need to redraw the rectangle?
A workaround (not a solution) for improving two of the remaining issues:

For
C#
private void SelectItemsInRectangle(Rect selectionRect, DataGrid dataGrid, object sender, MouseEventArgs e)

you should use the version which was posted here as "Solution 2".

The only problem seems to me when dragging the mouse form start point at bottom of the grid and the (mouse) endpoint on top is on a column header.

The rectangle then ends on the col header and the selection misses the invisible rows from the bottom.

A workaround which improves this, would be disable dataGrid.SelectedItems.Clear()
C#
private void SelectItemsInRectangle(Rect selectionRect, DataGrid dataGrid, object sender, MouseEventArgs e)
{
    //First, clear your current selection...
    //dataGrid.SelectedItems.Clear();


Another issue is case a cell is selected or in edit mode.

Then the rows selection can be uncomplete.

Here is my workaround:

C#
private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
       {

           var selectionStart = e.GetPosition(FileDataGrid);

           if (isSelecting == false)
           {
               if (FileDataGrid.SelectedCells.Count > 0)
               {
                   var curCell = FileDataGrid.CurrentCell;
                   FileDataGrid.UnselectAllCells();
                   if (curCell != null)
                   { FileDataGrid.CurrentCell = curCell; }
               }
           }

           selectionStartPoint = selectionStart;
           //selectionStartPoint = e.GetPosition(FileDataGrid);

           isleftbuttonpressed = true;
           isSelecting = true;

           adorner = new SelectionAdorner(FileDataGrid, selectionStartPoint);
           var adornerLayer = AdornerLayer.GetAdornerLayer(FileDataGrid);
               adornerLayer.Add(adorner);
               adorner.SetStartPoint(selectionStartPoint);

       }


open issue:

When you press left mouse button and drag the mouse while using the mouse wheel for scrolling, the rows selection fails.

Perhaps disable mouse wheel while selecting?


Window size too small, I use:
Title="MainWindow"
Width="935"
Height="650"
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900