简体   繁体   中英

How can I clear the Graphics before drawing another letter?

    private void timer1_Tick(object sender, EventArgs e)
    {
        counter--;
        DrawLetter();
        if (counter == 0)
        {
            t.Stop();
            TakeScreenShot();
        }
    }

    private void DrawLetter()
    {
        var letter = counter.ToString();
        Graphics g = Graphics.FromHdc(GetDC(IntPtr.Zero));
        float width = ((float)this.ClientRectangle.Width);
        float height = ((float)this.ClientRectangle.Width);
        float emSize = height;
        Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
        font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
        SizeF size = g.MeasureString(letter.ToString(), font);
        g.DrawString(letter, font, new SolidBrush(Color.White), (width - size.Width) / 2, 0);
    }

    private Font FindBestFitFont(Graphics g, String text, Font font, Size proposedSize)
    {
        // Compute actual size, shrink if needed
        while (true)
        {
            SizeF size = g.MeasureString(text, font);

            // It fits, back out
            if (size.Height <= proposedSize.Height &&
                 size.Width <= proposedSize.Width) { return font; }

            // Try a smaller font (90% of old size)
            Font oldFont = font;
            font = new Font(font.Name, (float)(font.Size * .9), font.Style);
            oldFont.Dispose();
        }
    }

    void TakeScreenShot()
    {
        bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
        gfxScreenshot = Graphics.FromImage(bmpScreenshot);
        gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
        bmpScreenshot.Save(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\ScreenCaptures\newfile.png", ImageFormat.Png);            
    }

I am able to draw the string but it is writing on top of itself.

How can I clear it? Basically I want the countdown to appear on the screen then take a screenshot.

Right now the number is overwritten by another.

我目前的结果

You can do the following: create an additional transparent form, and it will display timer values. This will allow you to erase the previous value. In addition, this will allow to get rid of the function call GetDC via PInvoke.

Form timerForm; // main form field
// Create and show additional transparent form before starting the timer
timerForm = new Form
{
    FormBorderStyle = FormBorderStyle.None,
    WindowState = FormWindowState.Maximized,
    TransparencyKey = SystemColors.Control,
    ShowInTaskbar = false
};
timerForm.Show();
timer.Start();

Change the method DrawLetter as follows

private void DrawLetter()
{
    var letter = counter.ToString();
    Graphics g = timerForm.CreateGraphics();

    float width = ClientRectangle.Width;
    float height = ClientRectangle.Width;
    float emSize = height;

    using (Font font1 = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular))
    using (Font font2 = FindBestFitFont(g, letter, font1, ClientRectangle.Size))
    using (var brush = new SolidBrush(Color.White))
    {
        SizeF size = g.MeasureString(letter, font2);
        g.Clear(SystemColors.Control);
        g.DrawString(letter, font2, brush, (width - size.Width) / 2, 0);
    }
}

We must release all used resources like fonts and brushes. For this I applied using .

Change the timer tick event handler as follows

private void timer1_Tick(object sender, EventArgs e)
{
    counter--;
    DrawLetter();
    if (counter == 0)
    {
        timer.Stop();
        TakeScreenShot();

        timerForm.Dispose(); // must release
    }
}

FindBestFitFont and TakeScreenShot methods remain unchanged.

Draw your font to a different bitmap. Transparent background (or whatever doesn't invert, see below - perhaps black).

(now you could also draw it with a different colored shadow to mitigate drawing on similar colored background - but the natures of SRCINVERT/XOR, below, will mitigate this as well)

Use BitBlt to copy it to the screen

Use the SRCINVERT raster op. (note: the colors may be different as it is XORing it with pixels underneath)

Now when is is time to erase, just make the same bitblt with the same contents as previous , the double XOR effect caused by SRCINVERT will have the effect of erasing it.

Then draw the next font.

Note: if desktop is updated between calls, all bets are off.

better...

Rather than attempting a transparent background, draw it on a white background. This will eliminate contrast issues with the font, eliminate concern with dynamic updates, and eliminate problems with erasing. Sometimes you have to admit - the method & code isn't the problem, the requirements are the problem. This all depends of course on the source of the requirements, etc.

If it needs to look professional, don't put the content on the screen, draw it after you take the screen capture.

If you end up using the transparent window approach, the screen shot may miss the transparent window. To get it, see this question: Capture screenshot Including Semitransparent windows in .NET . (could be fixed by newer .net / newer windows versions)

You need to invalidate all the windows on the desktop by using the InvalidateRect function to erase the previously drawn letter.

See additional codes below for the DrawLetter method.

[DllImport("user32")]
private static extern bool InvalidateRect(IntPtr hwnd, IntPtr rect, bool bErase);

private void DrawLetter()
{
    var letter = counter.ToString();
    Graphics g = Graphics.FromHdc(GetDC(IntPtr.Zero));
    float width = ((float)this.ClientRectangle.Width);
    float height = ((float)this.ClientRectangle.Width);
    float emSize = height;
    Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
    font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
    SizeF size = g.MeasureString(letter.ToString(), font);

    // Invalidate all the windows.
    InvalidateRect(IntPtr.Zero, IntPtr.Zero, true);

    // Sometimes, the letter is drawn before the windows are invalidated.
    // To fix that, add a small delay before drawing the letter.
    System.Threading.Thread.Sleep(100);

    // Finally, draw the letter.
    g.DrawString(letter, font, new SolidBrush(Color.White), (width - size.Width) / 2, 0);
}

A solution is:

You must take a snapshot of that area you want to show counter before all things. Then call DrawImage function to draw snapshot image before call DrawString function every time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM