选择矩形在滚动期间不随 WPF 数据网格内容移动

问题描述 投票:0回答:1

我正在尝试在 WPF DataGrid 中复制 Windows 文件资源管理器的选择行为。具体来说,我的目标是实现一个模仿文件资源管理器选择机制功能的选择矩形。目前,我创建了一个装饰矩形来方便选择项目。但是,当我尝试在滚动时选择行时遇到问题。当 DataGrid 的内容在其自己的 ScrollViewer 内滚动时,与选择矩形不相交的行将被取消选择,因为选择矩形是静态的,并且在滚动期间不会随数据网格内容一起移动。就像矩形在空中一样,没有与数据网格粘贴在一起。为了让您更清楚地了解该问题,我在下面提供了图片以供参考。

screenshot

我尝试了多种解决方案,但没有任何效果。我期望这个矩形在滚动期间应与数据网格内容一起移动,正如我们在 Windows 文件资源管理器中看到的那样。

  `<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 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> 
        
        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;
    
             }
    
         }
    
     }
    } `
c# wpf datagrid adorner microsoft-file-explorer
1个回答
0
投票

由于

DataGrid
已经支持使用鼠标按下+鼠标移动进行多选,因此您只需处理装饰器剪辑到可滚动区域(
ScrollContentPresenter
)。

我建议扩展

DataGrid
并将装饰器处理移至此自定义
DataGrid

请参阅完整示例,包括修复的

FileExplorer
(树结构中存在一些错误),位于 GitHub:DataGrid_Drag_Select_Example。 这只是原始代码。您可能必须改进错误方面的行为,并且您想要美化外观。但是,该示例是可用的并且是一个很好的起点。

装饰器.cs

public partial class SelectionAdorner : Adorner
{
  private Point startPoint;
  private Point endPoint;

  public SelectionAdorner(UIElement adornedElement)
    : base(adornedElement)
  {
    this.startPoint = new Point(0, 0);
    this.endPoint = new Point(0, 0);
    this.IsHitTestVisible = false;
  }

  protected override void OnRender(DrawingContext drawingContext)
  {
    var rectangle = new Rect(this.startPoint, this.endPoint);
    drawingContext.DrawRectangle(new SolidColorBrush(Colors.LightBlue) { Opacity = 0.5 }, null, rectangle);
  }

  public void SetStartPoint(Point point)
  {
    this.startPoint = point;
    this.startPoint = ClipLocation(this.startPoint);

    InvalidateVisual();
  }

  public void SetEndPoint(Point point)
  {
    this.endPoint = point;
    this.endPoint = ClipLocation(this.endPoint);

    InvalidateVisual();
  }

  public void Move(double horizontalOffset, double verticalOffset)
  {
    this.startPoint.Offset(horizontalOffset, verticalOffset);
    this.startPoint = ClipLocation(this.startPoint);

    InvalidateVisual();
  }

  // Clip new location:
  // Y = 0 < location_Y < adorned_element_height
  // X = 0 < location_X < adorned_element_width
  private Point ClipLocation(Point point)
  {
    double horizontalOffset = point.X;
    double coercedHorizontalOffset = Math.Min(Math.Max(0, horizontalOffset), ((FrameworkElement)this.AdornedElement).ActualWidth);

    double verticalOffset = point.Y;
    double coercedVerticalOffset = Math.Min(Math.Max(0, verticalOffset), ((FrameworkElement)this.AdornedElement).ActualHeight);

    return new Point(coercedHorizontalOffset, coercedVerticalOffset);
  }
}

DragSelectDataGrid.cs

public class DragSelectDataGrid : DataGrid
{
  private SelectionAdorner selectionAdorner;
  private AdornerLayer adornerLayer;
  private double actualRowHeight;
  private ScrollViewer scrollViewer;
  private ScrollContentPresenter scrollContentPresenter;
  private bool canPerfromDragSelect;

  public DragSelectDataGrid()
  {
    this.Loaded += OnLoaded;
  }

  private void OnLoaded(object sender, RoutedEventArgs e)
  {
    this.adornerLayer = AdornerLayer.GetAdornerLayer(this);
    if (this.adornerLayer == null)
    {
      throw new InvalidOperationException("No AdornerDecorator found in the parent tree.");
    }
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();
    if (!TryFindVisualChild(this, out this.scrollViewer))
    {
      throw new InvalidOperationException("No ScrollViewer found");
    }

    this.scrollViewer.ScrollChanged += OnScrollChanged;
    if (!scrollViewer.IsLoaded)
    {
      this.scrollViewer.Loaded += OnScrollViewerLoaded;
    }
    else
    {
      EnsureScrollContentPresenter();
    }
  }

  protected override void OnLoadingRow(DataGridRowEventArgs e)
  {
    base.OnLoadingRow(e);
    if (e.Row.ActualHeight == 0 || this.actualRowHeight == 0)
    {
      return;
    }

    this.actualRowHeight = e.Row.ActualHeight;
  }

  protected override void OnBeginningEdit(DataGridBeginningEditEventArgs e)
  {
    base.OnBeginningEdit(e);
    this.canPerfromDragSelect = false;
  }

  protected override void OnCellEditEnding(DataGridCellEditEndingEventArgs e)
  {
    base.OnCellEditEnding(e);
    this.canPerfromDragSelect = true;
  }

  private void OnScrollViewerLoaded(object sender, RoutedEventArgs e)
    => EnsureScrollContentPresenter();

  private void EnsureScrollContentPresenter()
  {
    if (!TryFindVisualChild(this.scrollViewer, out this.scrollContentPresenter))
    {
      throw new InvalidOperationException("No valid ScrollViewer found");
    }

    this.scrollContentPresenter.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
    this.scrollContentPresenter.PreviewMouseMove += OnPreviewMouseMove;
    this.scrollContentPresenter.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;

    this.selectionAdorner = new SelectionAdorner(this.scrollContentPresenter);
  }

  private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
  {
    bool canContentScroll = ScrollViewer.GetCanContentScroll(this);
    double verticalOffset = canContentScroll
      ? this.actualRowHeight * e.VerticalChange
      : e.VerticalChange;

    double horizontalOffset = e.HorizontalChange;

    this.selectionAdorner?.Move(-horizontalOffset, -verticalOffset);
  }

  private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  {
    if (!this.canPerfromDragSelect)
    {
      return;
    }

    if (this.actualRowHeight == 0 && this.HasItems)
    {
      var rowItemContainer = (FrameworkElement)this.ItemContainerGenerator.ContainerFromIndex(0);
      this.actualRowHeight = rowItemContainer.ActualHeight;
    }

    Point selectionStartPoint = e.GetPosition(this.scrollContentPresenter);
    this.selectionAdorner.SetStartPoint(selectionStartPoint);
    this.selectionAdorner.SetEndPoint(selectionStartPoint);
    this.adornerLayer.Add(this.selectionAdorner);
  }

  private void OnPreviewMouseMove(object sender, MouseEventArgs e)
  {
    base.OnPreviewMouseMove(e);
    if (e.LeftButton != MouseButtonState.Pressed)
    {
      return;
    }

    Point selectionEndPoint = e.GetPosition(this.scrollContentPresenter);
    this.selectionAdorner.SetEndPoint(selectionEndPoint);
  }

  private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  {
    base.OnPreviewMouseLeftButtonUp(e);
    this.adornerLayer.Remove(this.selectionAdorner);
  }

  private bool TryFindVisualChild<TChild>(DependencyObject parent, out TChild child)
    where TChild : DependencyObject
  {
    child = null;

    if (parent is Popup popup)
    {
      parent = popup.Child;
    }

    if (parent == null)
    {
      return false;
    }

    for (int childElementIndex = 0; childElementIndex < VisualTreeHelper.GetChildrenCount(parent); childElementIndex++)
    {
      DependencyObject childElement = VisualTreeHelper.GetChild(parent, childElementIndex);
      if (childElement is TChild resultChildElement)
      {
        child = resultChildElement;

        return true;
      }

      if (TryFindVisualChild(childElement, out child))
      {
        return true;
      }
    }

    return false;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.