c# - WPF DataGrid 中类似 Excel 的选择矩形

标签 c# wpf excel datagrid

这个问题类似于Excel like drag selection in Wpf itemscontrol ,但我想在 DataGrid 中执行此操作,而不是 ListBox。
我试图使 WPF DataGrid 中的选择矩形看起来像 Excel 中的矩形,包括右下角的黑色小方 block ,并且每个选定单元格周围没有边框。 谁能在 WPF DataGrid 中提供这方面的工作示例?

enter image description here

最佳答案

首先,您必须使用 this answer 中描述的方法隐藏焦点单元格的边框。 :

<DataGrid.CellStyle>
   <Style TargetType="DataGridCell">
      <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
   </Style>
</DataGrid.CellStyle>

然后您需要使用 DataGridOnSelectedCellsChanged 事件来添加/删除将绘制选择矩形的装饰器。对于填充句柄,您可以使用 Thumb,它会为您处理拖放操作:

    void DataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
    {
        var datagrid = (System.Windows.Controls.DataGrid)sender;
        AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(datagrid);
        if (datagrid.SelectedCells.Any())
        {
            DataGridCellInfo firstCellInfo = datagrid.SelectedCells.First();
            FrameworkElement firstElt = firstCellInfo.Column.GetCellContent(firstCellInfo.Item);

            DataGridCellInfo lastCellInfo = datagrid.SelectedCells.Last();
            FrameworkElement lastElt = lastCellInfo.Column.GetCellContent(lastCellInfo.Item);

            if (firstElt != null && lastElt != null)
            {
                var firstcell = (DataGridCell)firstElt.Parent;
                var lastCell = (DataGridCell) lastElt.Parent;
                Point topLeft = datagrid.PointFromScreen(firstcell.PointToScreen(new Point(0, 0)));
                Point bottomRight = datagrid.PointFromScreen(lastCell.PointToScreen(new Point(lastCell.ActualWidth, lastCell.ActualHeight)));
                var rect = new Rect(topLeft, bottomRight);
                if (fillHandleAdorner == null)
                {
                    fillHandleAdorner = new FillHandleAdorner(datagrid, rect);
                    adornerLayer.Add(fillHandleAdorner);
                }
                else
                    fillHandleAdorner.Rect = rect;
            }
        }
        else
        {
            adornerLayer.Remove(fillHandleAdorner);
            fillHandleAdorner = null;
        }
    }

FillHandleAdorner 类的代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace DataGrid
{
    internal class FillHandleAdorner : Adorner
    {
        const int SIZE = 5;

        Rect rect;

        readonly Thumb thumb;

        readonly VisualCollection visualChildren;

        Point mousePosition;

        System.Windows.Controls.DataGrid dataGrid;

        public Rect Rect
        {
            get { return rect; }
            set
            {
                rect = value;
                UpdateThumbPos(rect);
                mousePosition = rect.BottomRight;
                InvalidateVisual();
            }
        }

        public FillHandleAdorner(UIElement adornedElement, Rect rect)
            : base(adornedElement)
        {
            dataGrid = (System.Windows.Controls.DataGrid)adornedElement;
            Rect = rect;

            visualChildren = new VisualCollection(this);
            var border = new FrameworkElementFactory(typeof(Border));
            border.SetValue(Border.BackgroundProperty, Brushes.Black);
            thumb = new Thumb
            {
                Cursor = Cursors.Cross,
                Background = new SolidColorBrush(Colors.Black),
                Height = SIZE,
                Template = new ControlTemplate(typeof(Thumb)) { VisualTree = border },
                Width = SIZE
            };
            visualChildren.Add(thumb);

            UpdateThumbPos(rect);

            thumb.DragDelta += thumb_DragDelta;
        }

        void UpdateThumbPos(Rect rect)
        {
            if (thumb == null) return;
            mousePosition = rect.BottomRight;
            mousePosition.Offset(-SIZE/2 - 1, -SIZE/2 - 1);
            thumb.Arrange(new Rect(mousePosition, new Size(SIZE, SIZE)));
        }

        void thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            mousePosition.Offset(e.HorizontalChange, e.VerticalChange);
            IInputElement inputElt = dataGrid.InputHitTest(mousePosition);
            var tb = inputElt as TextBlock;
            if (tb == null) return;
            Point bottomRight = dataGrid.PointFromScreen(tb.PointToScreen(new Point(tb.ActualWidth + 1, tb.ActualHeight + 1)));
            Rect = new Rect(rect.TopLeft, bottomRight);
        }

        protected override int VisualChildrenCount
        {
            get { return visualChildren.Count; }
        }

        protected override Visual GetVisualChild(int index)
        {
            return visualChildren[index];
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            var blackSolidBrush = new SolidColorBrush(Colors.Black);
            var pen = new Pen(blackSolidBrush, 3);
            pen.Freeze();
            double halfPenWidth = pen.Thickness / 2;

            Rect rangeBorderRect = rect;
            rangeBorderRect.Offset(-1, -1);

            GuidelineSet guidelines = new GuidelineSet();
            guidelines.GuidelinesX.Add(rangeBorderRect.Left + halfPenWidth);
            guidelines.GuidelinesX.Add(rangeBorderRect.Right + halfPenWidth);
            guidelines.GuidelinesY.Add(rangeBorderRect.Top + halfPenWidth);
            guidelines.GuidelinesY.Add(rangeBorderRect.Bottom + halfPenWidth);

            Point p1 = rangeBorderRect.BottomRight;
            p1.Offset(0, -4);
            guidelines.GuidelinesY.Add(p1.Y + halfPenWidth);

            Point p2 = rangeBorderRect.BottomRight;
            p2.Offset(-4, 0);
            guidelines.GuidelinesX.Add(p2.X + halfPenWidth);

            drawingContext.PushGuidelineSet(guidelines);

            var geometry = new StreamGeometry();
            using (StreamGeometryContext ctx = geometry.Open())
            {
                ctx.BeginFigure(p1, true, false);
                ctx.LineTo(rangeBorderRect.TopRight, true, false);
                ctx.LineTo(rangeBorderRect.TopLeft, true, false);
                ctx.LineTo(rangeBorderRect.BottomLeft, true, false);
                ctx.LineTo(p2, true, false);
            }
            geometry.Freeze();
            drawingContext.DrawGeometry(null, pen, geometry);

            drawingContext.Pop();
        }
    }
}

代码不完整,因为它是针对一个尚未开始的项目,但它应该可以帮助您开始。

关于c# - WPF DataGrid 中类似 Excel 的选择矩形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18925864/

相关文章:

c# - 在 LINQ to Entities (EF4.1) 中返回具有类型约束的泛型输入类型

c# - 在 WPF 中找不到附加属性

wpf - 路由事件和附加事件之间的区别以及它们有什么区别?

VBA循环遍历所有图表中的所有系列

excel - 基于第二列值的动态命名范围,没有 VBA

vba - 在 Excel 中自动添加加载项的单个菜单项

c# - MVC 4 Web API 中的 URL 参数

c# - SqlCommand.Prepare 方法要求所有参数都具有显式设置的类型

c# - 我可以加快此查询以检索域中的所有计算机吗?

c# - 在 VS 2015 和 EF7 从模型生成 SQLite 数据库