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);
}
}
}
Selection Rectangle not moving along with datagrid content during scroll.
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.
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);
}
-
gekka 9,666 Reputation points MVP
2024-04-13T03:39:30.33+00:00