C# redraw blinking - c#

I have UserControl : Panel. When I add to Form own UserControl. UserControl.Anchor = Left|Right|Top|Bottom. When I resize Form Rectangle is blinking. How can you make that do not blink?
public partial class UserControl1 : Panel
{
public UserControl1()
{
InitializeComponent();
this.ResizeRedraw = true;
}
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
Pen pen = new Pen(Color.Black, 1);
Brush brush = new SolidBrush(Color.Black);
g.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);
pen.Dispose();
}
}
}

There are many things you can do to reduce flicker:
make your paint handler as fast as possible by moving unnecessary work out of the method (e.g. create brushes and pens outside the method and cache them to avoid the creation cost on every paint; clear the background using gfx.Clear rather than filling the rectangle; redesign your display so you don't have to draw so much stuff; cache parts of the image in bitmaps so they can be redrawn faster)
avoid drawing each pixel more than once (if you fill the background with white and then draw over it with black, it will flicker. But if you draw awhite rectangle and then draw a frame around it in black you can avoid the flicker)
only draw the visible part of your graphics by checking the clip rectangle, to avoid the work of drawing stuff that isn't currently visible)
ensure that the framework is not filling the background for you (usually in white) by overriding the erase background handling.
enable double buffering so that any remaining flicker is eliminated. This uses a lot more resources than the other approaches, although these days that's not usually much of a problem.
use red existing e.Graphics to draw with rather than calling CreateGraphics

Try set doubleBuffered = true and you do not have to create graphics object in pain event. you can get that from the event args. You have to make sure you do minimum amount of task in a paint event.
public partial class UserControl1 : Panel
{
public UserControl1()
{
InitializeComponent();
this.ResizeRedraw = true;
this.DoubleBuffered = true;
}
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
Pen pen = new Pen(Color.Black, 1);
Brush brush = new SolidBrush(Color.Black);
g.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);
}
}

Related

How to repaint certain part of panel / or use two panels on top of each other with the top one having transparent background?

I have a C# WinForms project where I have to paint some things on my panel. I load in a grid from a file that translates to squares that are painted on the panel. Then I load in a file with dots that are then painted on top of the squares.
I then have a function that moves the dots around. But the repaint function is called every tick which causes the whole grid to flicker continously because it is painted so quick after each other.
How do I make it so that only the dots are repainted?
The repaint and paint functions are as follows:
private void Repaint(object sender, EventArgs e)
{
GridPanel.Invalidate();
}
private void GridPanel_Paint(object sender, PaintEventArgs e)
{
if ((GridPanel.Width != grid.Width || GridPanel.Height != grid.Height))
{
grid.Height = GridPanel.Height;
grid.Width = GridPanel.Width;
grid.setDimensions();
}
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, grid.Width, grid.Height));
foreach(Square square in grid.Squares)
{
if (square.Letter != '_')
{
SolidBrush color = new SolidBrush(square.Color);
g.FillRectangle(color, new Rectangle(square.X * grid.squareWidth, square.Y * grid.squareHeight, grid.squareWidth, grid.squareHeight));
}
}
foreach(Artist artist in grid.Artists)
{
SolidBrush color = new SolidBrush(artist.Color);
g.FillRectangle(color, new Rectangle(Convert.ToInt32(artist.X * grid.squareWidth), Convert.ToInt32(artist.Y * grid.squareHeight), grid.artistWidth, grid.artistHeight));
}
}
I also tried to use a second panel for the dots so I only have to repaint that one. But I cant get a transparent background working on the second panel, so the first panel is not visible this way.
Somebody knows a good solution for this problem?

scrolling in a ScrollableControl: is there a .NET variant of MFC ViewPort?

I'm learning how to draw in Winforms. I've created a Form, with a panel with scrollbars. Upone Event Paint I draw an ellipse. This is fairly straightforward:
this.panel1.AutoScroll = true;
this.panel1.AutoScrollMinSize = = new System.Drawing.Size(500, 300);
private void OnPaint(object sender, PaintEventArgs e)
{
Rectangle ellipse = new Rectangle(Point.Empty, new Size(400, 400));
ellipse.Offset(this.panel1.AutoScrollPosition);
using (Pen myPen = new System.Drawing.Pen(System.Drawing.Color.Red))
{
e.Graphics.DrawEllipse(myPen, ellipse);
}
}
private void OnPanelScroll(object sender, ScrollEventArgs e)
{
this.panel1.Invalidate();
}
This works fine, but the complete image is redrawn when resizing or scrolling the panel.
A long time ago, in the time of MFC there was the notion of ViewPort / SetViewPortOrg / mapping modes / etc.. Scrolling and resizing did not require a recalculation of the complete image. Once you had drawn the image you didn't have to redraw as long as the complete image was not changed. All you had to do was move the viewport, or change the mapping mode
Does .NET have something similar that can do the scrolling for me? Maybe I should not draw on a panel, but on another subclass of a ScrollableControl?

Highlight the Rectangular Area while Dragging it

I am creating a image viewer sort of application. I am on Windows and using .Net
In my app, I am trying to highlight a Particular area while dragging.
I have created a Rectangle.
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
Now I am dragging this rectangular area along with my mouse movements.
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
MoveRect(ptNew);
ptOld = ptNew;
}
Here I am trying to move this rect along with my mouse
void MoveRect(Point point)
{
Graphics grfxClient = CreateGraphics();
Rectangle tempRectangle = new Rectangle(areaRect.Left, areaRect.Top, areaRect.Width, areaRect.Height);
grfxClient.DrawRectangle(rectPen, tempRectangle);
this.Invalidate();
grfxClient.Dispose();
}
My Code till this point is working fine.
Now I would like to darken the INVERSE drag Area (The area which is outside the drag region), I mean the area which is within this Rectangle should gets highlighted while dragging.
Any idea how to proceed.
Thanks.
-Pankaj
I suppose you can do it by creating a Region object that covers the outside of the rectangle and fill it with a semi-transparent SolidBrush to make it look darkened.
You also don't have to create a graphics and draw in OnMouseMove event, but just shift the rectangle and invalidate the surface of the control you are drawing on.
The code I used looks more or less like this:
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
//A new field with a semi-transparent brush to paint the outside of the rectangle
Brush dimmingBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 0));
protected override void OnPaint(PaintEventArgs e)
{
Region outsideRegion = new System.Drawing.Region(e.ClipRectangle);
outsideRegion.Exclude(areaRect);
Graphics dcPaint = e.Graphics;
dcPaint.FillRegion(dimmingBrush, outsideRegion);
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
ptOld = ptNew;
this.Invalidate();
}
The method named MoveRect is not needed.
It now seems to work as you wanted it to.
Suggestions
I also have some suggestions. You don't have to use them, maybe they will be helpful for you.
You haven't written what kind of control you are using to draw on (or overriding Form methods and painting directly on it), but I suggest you to use a PictureBox control, create a custom control derived from it and override its events. This should make the painting process smooth and prevent flickering. To do it this way:
Create a new user control by selecting Add and User Control... and name a new control i.e. MyPictureBox
change the parent class of the control, so it should now contain the line:
public partial class MyPictureBox : PictureBox
open file MyPictureBox.Designer.cs and comment out these lines:
//this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
//this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
copy the code I posted in this answer and add line base.OnPaint(e); and the beginning of OnPaint method
compile the project
now you should be able to open designer of your main form, drag MyPictureBox control from the toolbox and use it without additional code needed
You also might consider changing the behaviour of the highlighted area, so mouse cursor was in the center of it. I suppose it would be more intuitive to the user.
If you have any issues with the code, just write it in the comments and I'll try to help :).

How to draw a circle on a form that covers the whole working area?

How to draw a circle on a form that covers the whole working area?
I have tried the following code. But when I re-size the form, the circle is distorted.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
Pen redPen = new Pen(Color.Red, 3);
Rectangle rect = new Rectangle(0,0, this.ClientSize.Width, this.ClientSize.Height);
g.DrawEllipse(redPen, rect);
}
}
You should hook into the ClientSizeChanged event as well to trigger a redraw.
What currently happens is that Windows assumes that only the small portion which became visible needs to be redrawn, and clips everything else off. You therefore need to invalidate the full form (Invalidate()) when a resize takes place.
If the circle starts flickering when resizing, enable double buffering of the form.
Try to set the DoubleBuffered property of the Form to true.

Problem with CreateGraphics and drawing strings

The control below draws a string in a rectangle. On mouse move there is a hit test on the string rectangle, and the string is redrawn via CreateGraphics. The irritating problem is that the text is not drawn the same as in the Paint handler; it appears to be displaced by about 1 pixel, and the effect is like a bold font. How can I create a graphics object exactly like the one in the Paint handler so the text is drawn the same way? Ordinarily you would invalidate and redraw everything in the Paint event, but I have potentially hundreds of of other drawing items and only want to draw the string. Should I try to do any drawing outside of the Paint event or is this a mistake?
Example control:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test.TestModes
{
public partial class ExampleControl: UserControl
{
private const string testString = "0123456789";
private RectangleF stringRect = new RectangleF(10, 10, 100, 20);
public ExampleControl()
{
InitializeComponent();
}
private void ExampleControl_Paint(object sender, PaintEventArgs e)
{
Font font = new Font("Arial", 12, FontStyle.Regular);
e.Graphics.DrawString(testString, font, Brushes.Black, stringRect);
font.Dispose();
}
private void DrawString(bool hit)
{
Font font = new Font("Arial", 12, FontStyle.Regular);
using(Graphics g = CreateGraphics())
{
g.SetClip(ClientRectangle);
if(hit)
g.DrawString(testString, font, Brushes.Red, stringRect);
else
g.DrawString(testString, font, Brushes.Black, stringRect);
}
font.Dispose();
}
private void ExampleControl_MouseMove(object sender, MouseEventArgs e)
{
if(stringRect.Contains(e.Location))
DrawString(true);
else
DrawString(false);
}
private void button1_Click(object sender, EventArgs e)
{
Invalidate();
}
}
}
It is the CreateGraphics() call that is getting you in trouble, indirectly. The problem is anti-aliasing of the text. A normal painting cycle erases the background before drawing something on top. That doesn't happen in your case, your draw text on top of existing text. The side effect is that the pixels uses to create the aliasing get darker each time your draw. The end result is bold looking, and noticeably jagged text outlines.
The fix is easy: start with a clean slate before you draw:
using (Graphics g = CreateGraphics()) {
g.Clear(this.BackColor); <=== added
g.SetClip(ClientRectangle);
// etc..
}
You'll now also get to encounter a problem in drawing known as "flicker". It might not yet be that noticeable yet, but it will when you do more drawing. Flicker is suppressed with double-buffering. A feature supported by Windows Forms, but only if you use standard drawing techniques. In other words: no CreateGraphics().

Categories

Resources