WinForms 创建透明的可清除 pictureBox 覆盖

WinForms create transparent clearable pictureBox overlays

我想在 WinFrom 应用程序中为 pictureBox 创建 2 个透明覆盖层,这样我就可以分别在两者上绘制,也可以在我想空白透明覆盖层时清除它。

我在一张叠加层上绘制矩形。我一直想要这些矩形。

在第二个叠加层上我画了圆,但我只想画 1 个圆,在用户输入后清除这个圆并画另一个。

目前我正在使用

var transparentOverlay = pictureBox.createGraphics();

但我不知道如何清除覆盖以空白透明图形。 我试过了

  1. transparentOverlay.Clear(Color.Transparent) 将所有叠加层变为黑色
  2. pictureBox.Invalidate() 清除了两个叠加层中的所有图形,因此我的矩形保留在原处
  3. 使用我在任何绘图之前创建的一些备份图形,并通过将此图形分配给它来清除我的叠加层 transparentOverlay = transparentOverlayBackup 但这没有任何作用,所有矩形和所有圆圈都保留在它们的位置

有没有办法创建粘在 pictureBox 上的可清除透明图形?

编辑:

我在那个图片框中有一张带文字的图片。而我想要做的是在文本的文字周围绘制矩形,并且这个矩形应该一直保留在图片上。

比我想画一个圆圈并等待用户点击屏幕。这一切都很好,但是当用户点击屏幕时,我想清除那个圆圈并绘制另一个。

//this method I call by click on a button to start annotation
private void ExpertAnnotate(object sender, EventArgs e)
{
    var pen = new Pen(Color.Black, 1);
    if (!annotationIsRunning) //check if annotation is in process or not
    {
        annotationIsRunning = true;

        annotationOverlay = pictureBox.CreateGraphics(); //create transparent overlay for drawing

        //draw rectangles around all words in text (AOIs)
        annotationAOIs.ForEach(a =>
        {
            annotationOverlay.DrawRectangle(pen, a.Start.X, a.Start.Y, (a.End.X - a.Start.X), (a.End.Y - a.Start.Y));
        });

        //subscribe mouseMove and mouseClick events on pictureBox
        pictureBox.MouseMove += HighlightAOI;
        pictureBox.MouseClick += SelectAOI;
    }

    //define brushes for drawing circles (fixations)
    var brush = new SolidBrush(Color.FromArgb(128, Color.BlueViolet));
    var dotBrush = new SolidBrush(Color.DarkBlue);
    pen.Color = Color.Blue;
    long sizeOfFixation;
    var f = Fixations[fixationCounter - 1]; //get current fixation to draw

    sizeOfFixation = (int)f.Length / FIX_SIZE_COEFICIENT; //compute size of circle
    annotationOverlay.FillEllipse(dotBrush, f.PosX - 1, f.PosY - 1, 3, 3);
    //draw fixation on overlay
    annotationOverlay.FillEllipse(brush, (f.PosX - sizeOfFixation), (f.PosY - sizeOfFixation), sizeOfFixation * 2, sizeOfFixation * 2);
}

//eventHandler for mouseMove - this method color rectangle over which mouse hover to red border
private void HighlightAOI(object sender, EventArgs e)
{
    //this just draw last highlighted rect to black when we not yet hover mouse above it
    if (lastHighlightedAOI != null)
    {
        annotationOverlay.DrawRectangle(new Pen(Color.Black, 1), lastHighlightedAOI.Start.X, lastHighlightedAOI.Start.Y, (lastHighlightedAOI.End.X - lastHighlightedAOI.Start.X), (lastHighlightedAOI.End.Y - lastHighlightedAOI.Start.Y));
    }
    //get position of mouse sursor
    var x = pictureBox.PointToClient(Cursor.Position).X;
    var y = pictureBox.PointToClient(Cursor.Position).Y;
    var tempFix = new Fixation() { PosX = x, PosY = y };

    //get rectangle over which mouse hover
    lastHighlightedAOI = tempFix.LiesIn(annotationAOIs).FirstOrDefault();

    if (lastHighlightedAOI != null)
    {
        //highlight rectangle by painting red border
        annotationOverlay.DrawRectangle(new Pen(Color.Red, 1), lastHighlightedAOI.Start.X, lastHighlightedAOI.Start.Y, (lastHighlightedAOI.End.X - lastHighlightedAOI.Start.X), (lastHighlightedAOI.End.Y - lastHighlightedAOI.Start.Y));
    }
}

//eventHandler for mouse click
private void SelectAOI(object sender, EventArgs e)
{
    //get position of cursor
    var x = MousePosition.X;
    var y = MousePosition.Y;
    var tempFix = new Fixation() { PosX = x, PosY = y };

    //get rectangle which we selected by a mouse click
    var aoi = tempFix.LiesIn(annotationAOIs).FirstOrDefault();

    //assign last shown fixation to selected rectangle
    if (aoi != null)
    {
        aoi.AssignedFixations.Add(Fixations[fixationCounter - 1]);
    }

    //if it wasn't last fixation again call ExpertAnnotation function to draw another Fixation over image (!!! here I need to clear last drawn fixation (circle) disappear and draw next fixation in ExpertAnnotate method)
    if (fixationCounter != Fixations.Count)
    {
        ExpertAnnotate(sender, e);
    }
    else
    {
        TerminateExpertAnnotation("regular");
    }
}

最好的方法是使用 PictureBox 控件的 OnPaint 事件处理程序并将所有绘图调用放在其中。

您可以使用传递给事件处理程序的 Graphics 对象来获取要绘制的表面(即图片框),然后使用各种方法绘制您想要的形状。

要绘制 'transparent' 形状,只需绘制轮廓而不是填充形状。

感谢 @Reza Aghaei 在聊天中指导我解决问题。

对我来说,可接受的解决方案是构建多层图像并将其分配给 pictureBox.Image 属性。

我通过从文件加载图像来构建图像:

Image im = new Bitmap(path); // loads image from file

然后从这个图像创建图形:

var g = Graphics.FromImage(im); // creates graphics from loaded image

将所有需要的矩形绘制到此图像并将此图像备份到某个全局 Image 实例:

var pen = new Pen(Color.Black, 1);
// draws all rectangles on the image
annotationAOIs.ForEach(a =>
{
    g.DrawRectangle(pen, a.Start.X, a.Start.Y, (a.End.X - a.Start.X), (a.End.Y - a.Start.Y));
});
g.Dispose(); // disposes used graphics
ImageBackup = new Bitmap(im); // backup image with rectangles

在上面的部分我创建了一个图像的静态部分,它不会改变并且我备份了它所以下次我将只从备份创建新的 Image 实例而不绘制任何矩形。

然后当我想在这张图片上显示新的圆圈时,我只是:

var image = new Bitmap(ImageBackup); // creates new instance of image with rectangles from backup
var g = Graphics.FromImage(image); // creates graphics from image

// in this part draw circle at specific point
var f = Fixations[fixationIndex];
sizeOfFixation = (int)f.Length / FIX_SIZE_COEFICIENT;
g.FillEllipse(dotBrush, f.PosX - 1, f.PosY - 1, 3, 3);
g.FillEllipse(brush, (f.PosX - sizeOfFixation), (f.PosY - sizeOfFixation), sizeOfFixation * 2, sizeOfFixation * 2);

pictureBox.Image.Dispose(); // dispose old pictureBox image
pictureBox.Image = image; // set new image

imageOverlay = pictureBox.CreateGraphics(); // get transparent graphics overlay for pictureBox so we can draw anything else over picture (in my case highlighting rectangles over which I hover a mouse)
g.Dispose(); // dispose used graphics