我正在尝试创建一个大网格,单元格已经绘制了一个可点击(点击事件)省略号。
我正在使用Canvas作为父级并将Ellipses添加到Children List但是当我尝试渲染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);
}
}
有什么想法吗?
答案 0 :(得分:2)
在效率方面,最轻盈的&#39;解决方案可能是将单个椭圆渲染到RenderTargetBitmap
,然后让自定义控件绘制一个带有平铺ImageBrush
的矩形,一遍又一遍地重复相同的椭圆。使用RenderTargetBitmap
的好处在于,您可以让开发人员将任何可视元素插入到tile中;它不需要是椭圆形。
您需要一种在屏幕坐标和(column, row)
对之间进行转换的方法。您将知道椭圆位图的大小,因此该部分应该非常简单。
要在鼠标悬停或按下椭圆时处理细微变化,您应该将一些剪辑几何图形推入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;
}
如果您想要像素完美的点击测试,则应将RenderTargetBitmap
复制到WriteableBitmap
,然后使用 WriteableBitmapEx 库来获取经过测试的颜色坐标并检查alpha是否为非零。
要提供点击事件通知,您需要处理常见的鼠标事件:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
e.Handled = CaptureMouse();
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (IsMouseCaptured &&
TryHitTest(e.GetPosition(this), out var column, out var row))
{
ReleaseMouseCapture();
e.Handled = true;
//
// Raise 'TileClick' event with 'column' and 'row'.
//
}
}
private (int x, int y) _lastHover;
protected override void OnMouseMove(MouseEventArgs e)
{
TryHitTest(e.GetPosition(this), out var x, out var y);
if (_lastHover.x != x || _lastHover.y != y)
InvalidateVisual();
_lastHover = (x, y);
}
由于我对如何描述渲染到矩形存在一些困惑,让我澄清一下:我不是在谈论绘制Rectangle
元素。我们的想法是创建一个执行自己渲染的自定义控件,并绘制使用平铺图像画笔绘制的矩形几何。您的OnRender
方法看起来像这样:
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
var regularBrush = /* regular cell tiled brush */;
var hoverBrush = /* hovered cell brush */;
var fullBounds = new Rect(/* full bounds */);
var hoverBounds = new Rect(/* bounds of hovered cell */);
var hasHoveredCell = /* is there a hovered cell? */;
if (hasHoveredCell)
{
// Draw everywhere *except* the hovered cell.
dc.PushClip(
Geometry.Combine(
new RectangleGeometry(fullBounds),
new RectangleGeometry(hoverBounds),
GeometryCombineMode.Exclude,
Transform.Identity));
}
dc.DrawRectangle(regularBrush , null, fullBounds);
if (hasHoveredCell)
{
// Pop the clip and draw the hovered cell.
dc.Pop();
dc.DrawRectangle(hoverBrush, null, hoverBounds);
}
}
答案 1 :(得分:1)
您正在限制WPF可以使用它的框架呈现的内容。省略号或所有绘图对象是具有相当多属性和事件的对象。渲染10,000将导致您滞后。
最好的办法是使用DrawingContext并创建更简单的方法,并创建自己的方法来跟踪点击的内容。