C# 渲染大型可单击网格wpf

C# 渲染大型可单击网格wpf,c#,wpf,canvas,optimization,drawing,C#,Wpf,Canvas,Optimization,Drawing,我正在尝试创建一个大网格,其中单元格绘制了一个可单击(单击事件)省略号 我使用画布作为父对象,并将省略号添加到子对象列表中,但当我尝试渲染100x100省略号时,它会滞后 我已经尝试使用DrawingVisual和DrawingContext渲染它们,但它们不可单击,并且我将无法存储省略号属性(颜色、笔划…) 呈现省略号的当前代码 for (int i = 0; i < this.grid.Cols; i++) { for (int j = 0; j < this.grid.

我正在尝试创建一个大网格,其中单元格绘制了一个可单击(单击事件)省略号

我使用画布作为父对象,并将省略号添加到子对象列表中,但当我尝试渲染100x100省略号时,它会滞后

我已经尝试使用DrawingVisual和DrawingContext渲染它们,但它们不可单击,并且我将无法存储省略号属性(颜色、笔划…)

呈现省略号的当前代码

for (int i = 0; i < this.grid.Cols; i++) {
    for (int j = 0; j < this.grid.Rows; j++) {
        SolidColorBrush fillBrush = Brushes.Red;
        SolidColorBrush strokeBrush = Brushes.Blue;

        Ellipse ellipse = new Ellipse() {
            Width = this.grid.Radius,
            Height = this.grid.Radius,
            Fill = fillBrush,
            Stroke = strokeBrush,
            StrokeThickness = 2
        };

        Canvas.SetTop(ellipse, (this.grid.Radius * j) + (j * this.grid.Margin));
        Canvas.SetLeft(ellipse, (this.grid.Radius * i) + (i * this.grid.Margin));

        children.Add(ellipse);
        parent.Children.Add(ellipse);
    }
}
for(int i=0;i


有什么想法吗?

您遇到了WPF框架的渲染限制。椭圆或所有图形对象都是具有许多特性和事件的对象。渲染10000将导致延迟


最好的办法是使用DrawingContext并创建您自己更简单的方法,并创建您自己的方法来跟踪单击的内容。

您遇到了WPF框架所能呈现内容的限制。椭圆或所有图形对象都是具有许多特性和事件的对象。渲染10000将导致延迟


最好的办法是使用DrawingContext并创建自己更简单的方法,并创建自己的方法来跟踪单击的内容。

就效率而言,最“轻量级”的解决方案可能是将单个椭圆渲染为
RenderTargetBitmap
,然后让一个自定义控件使用平铺的
ImageBrush
绘制一个矩形,以反复重复相同的椭圆。使用
RenderTargetBitmap
的好处在于,您可以让开发人员插入任何可视元素来平铺;它不必是椭圆

您需要一种方法在屏幕坐标和
(列、行)
对之间进行转换。您将知道椭圆位图的大小,因此该部分应该非常简单

要在鼠标悬停在椭圆上或按下椭圆时处理细微变化,应将一些剪辑几何体推入
DrawingContext
以绘制光标下包含椭圆的区域以外的所有位置。然后弹出遮罩,用另一个
ImageBrush
绘制一个矩形,其中包含处于悬停/按下状态的椭圆

要处理点击,您需要编写一些自定义点击测试逻辑。基本上,你会想要这样的东西:

private bool TryHitTest(Point p, out int column, out int row)
{
    column = -1;
    row = -1;

    if (p.X < 0 || p.X > ActualWidth || p.Y < 0 || p.Y > ActualHeight)
        return false;

    var image = /* your ellipse bitmap */;
    if (image == null)
        return false;

    var tileWidth = image.Width;
    var tileHeight = image.Height;

    var x = (int)(p.X % tileWidth);
    var y = (int)(p.Y % tileHeight);

    // If you want pixel-perfect hit testing, check the alpha channel.
    // Otherwise, skip this check.
    if (image.GetPixel(x, y).A == 0)
        return false;

    column = (int)Math.Floor(p.X / tileWidth);
    row = (int)Math.Floor(p.Y / tileHeight);

    return true;
}

增编:渲染 由于对如何描述矩形的渲染存在一些混淆,让我澄清一下:我不是在谈论绘制
矩形
元素。其想法是创建一个自定义控件来执行自己的渲染,并使用平铺图像笔刷绘制矩形几何体。您的
OnRender
方法如下所示:

private bool TryHitTest(Point p, out int column, out int row)
{
    column = -1;
    row = -1;

    if (p.X < 0 || p.X > ActualWidth || p.Y < 0 || p.Y > ActualHeight)
        return false;

    var image = /* your ellipse bitmap */;
    if (image == null)
        return false;

    var tileWidth = image.Width;
    var tileHeight = image.Height;

    var x = (int)(p.X % tileWidth);
    var y = (int)(p.Y % tileHeight);

    // If you want pixel-perfect hit testing, check the alpha channel.
    // Otherwise, skip this check.
    if (image.GetPixel(x, y).A == 0)
        return false;

    column = (int)Math.Floor(p.X / tileWidth);
    row = (int)Math.Floor(p.Y / tileHeight);

    return true;
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
var regularBrush=/*常规单元格平铺笔刷*/;
var hoverBrush=/*悬浮单元格刷*/;
var fullBounds=new Rect(/*full bounds*/);
var hoverBounds=新的矩形(/*悬停单元格的边界*/);
var hasHoveredCell=/*是否存在悬停单元格?*/;
if(hasHoveredCell)
{
//除悬浮单元格外,在任何地方绘制。
直流推杆(
几何。组合(
新矩形几何体(全界),
新矩形几何体(悬停边界),
GeometryCombineMode.Exclude,
转换(身份);
}
DrawRectangle(regularBrush,null,fullBounds);
if(hasHoveredCell)
{
//弹出剪辑并绘制悬停单元格。
dc.Pop();
DrawRectangle(hoverBrush,null,hoverBounds);
}
}

就效率而言,最“轻量级”的解决方案可能是将单个椭圆渲染为
RenderTargetBitmap
,然后让自定义控件使用平铺的
ImageBrush
绘制矩形,以反复重复相同的椭圆。使用
RenderTargetBitmap
的好处在于,您可以让开发人员插入任何可视元素来平铺;它不必是椭圆

您需要一种方法在屏幕坐标和
(列、行)
对之间进行转换。您将知道椭圆位图的大小,因此该部分应该非常简单

要在鼠标悬停在椭圆上或按下椭圆时处理细微变化,应将一些剪辑几何体推入
DrawingContext
以绘制光标下包含椭圆的区域以外的所有位置。然后弹出遮罩,用另一个
ImageBrush
绘制一个矩形,其中包含处于悬停/按下状态的椭圆

要处理点击,您需要编写一些自定义点击测试逻辑。基本上,你会想要这样的东西:

private bool TryHitTest(Point p, out int column, out int row)
{
    column = -1;
    row = -1;

    if (p.X < 0 || p.X > ActualWidth || p.Y < 0 || p.Y > ActualHeight)
        return false;

    var image = /* your ellipse bitmap */;
    if (image == null)
        return false;

    var tileWidth = image.Width;
    var tileHeight = image.Height;

    var x = (int)(p.X % tileWidth);
    var y = (int)(p.Y % tileHeight);

    // If you want pixel-perfect hit testing, check the alpha channel.
    // Otherwise, skip this check.
    if (image.GetPixel(x, y).A == 0)
        return false;

    column = (int)Math.Floor(p.X / tileWidth);
    row = (int)Math.Floor(p.Y / tileHeight);

    return true;
}

增编:渲染 由于对如何描述矩形的渲染存在一些混淆,让我澄清一下:我不是在谈论绘制
矩形
元素。其想法是创建一个自定义控件来执行自己的渲染,并使用平铺图像笔刷绘制矩形几何体。您的
OnRender
方法如下所示:

private bool TryHitTest(Point p, out int column, out int row)
{
    column = -1;
    row = -1;

    if (p.X < 0 || p.X > ActualWidth || p.Y < 0 || p.Y > ActualHeight)
        return false;

    var image = /* your ellipse bitmap */;
    if (image == null)
        return false;

    var tileWidth = image.Width;
    var tileHeight = image.Height;

    var x = (int)(p.X % tileWidth);
    var y = (int)(p.Y % tileHeight);

    // If you want pixel-perfect hit testing, check the alpha channel.
    // Otherwise, skip this check.
    if (image.GetPixel(x, y).A == 0)
        return false;

    column = (int)Math.Floor(p.X / tileWidth);
    row = (int)Math.Floor(p.Y / tileHeight);

    return true;
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
var regularbush=/*常规单元格平铺笔刷