C# Form Draws Slowly - c#

I made an application on Visual Studio 2012 and im trying to speed up the draw times of the forms.
I have a main form and inside of it i have a container in which depending on the selection of a tool strip, the new form will show inside of it. It works like a charm, but the issue is, it takes a lot of time to draw, no matter how good the computer is (tried on different computers), and the issue seems to be the background.
I have set a background image for the main form, for the container inside that form, and for all the forms in my project, so when they show up, the background image isnt chopped and it continues the image. But, if instead of using a background for picture and i leave the back in white, for all, the main form, container, and forms, it works like a charm.
I've read around the internet about setting the double buffer inside the form and stuff to true, but it didnt do anything, it takes the same ammount of time.
Any advice? Thanks in advance!

You can squeeze a little more speed out of it by drawing the background manually. This helps because it allows you to disable the underlying background color, which just wastes time because it gets overwritten with the image anyway.
// Reference to manually-loaded background image
Image _bmp;
// In your constructor, set these styles to ensure that the background
// is not going to be automatically erased and filled with a color
public Form1() {
InitializeComponent();
SetStyle(
ControlStyles.Opaque |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
// Load background image
_bmp = Image.FromFile("c:\\path\\to\\background.bmp");
}
// Override OnPaint to draw the background
protected override void OnPaint(PaintEventArgs e) {
var g = e.Graphics;
var srcRect = new Rectangle(0, 0, _bmp.Width, _bmp.Height);
int startY = Math.Max(0, (e.ClipRectangle.Top / _bmp.Height) * _bmp.Height);
int startX = Math.Max(0, (e.ClipRectangle.Left / _bmp.Width) * _bmp.Width);
for (int y = startY; y < e.ClipRectangle.Bottom; y+= _bmp.Height)
for (int x = startX; x < e.ClipRectangle.Right; x += _bmp.Width)
{
var destRect = new Rectangle(x, y, _bmp.Width, _bmp.Height);
g.DrawImage(_bmp, destRect, srcRect, GraphicsUnit.Pixel);
}
base.OnPaint(e);
}

Related

Drawing simultaneously on two controls in two windows without delay

In my application, there are 2 windows and both contain a PictureBox. The first (pb1) allows interaction and the image can be changed through click- and mouseMove-events. These events call pb1.Invalidate(); which works fine.
I want the second PictureBox (pb2) to redraw as well so I call pb2.Invalidate() from the paint-event of pb1. [Just for context, the second PictureBox shows nearly the same Image but on a bigger scale and some parts of the drawing will be left out in the future so I use the same Method in both paint events which decides what to draw and what not]
It works but it's "laggy" and I want it to be as smooth as the paint on the first PictureBox. I reduced the paint event just to a grid for test purposes.
Both windows are double buffered.
I tried replacing the picture boxes with SKGLControls from SkiaSharp (which should have better performance). The example code still uses the SkiaEvents so don't be confused if the problem occurs with both controls.
I tried to use .Update() or .Refresh() instead of .Invalidate() but i guess its to much to handle, the application just crashes..
Here is the method that is called by both OnPaint events
public void Update(SKPaintGLSurfaceEventArgs e, bool bigscreen)
{
SKCanvas canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Beige);
//Zoom to specified area
SKMatrix matrix = SKMatrix.Identity;
if (!bigscreen)
{
matrix = matrix.PostConcat(SKMatrix.CreateScale(canvasSize / (float)zoomArea.Width, canvasSize / (float)zoomArea.Height));
}
else
{
matrix = matrix.PostConcat(SKMatrix.CreateScale(bigCanvasSize / (float)zoomArea.Width, bigCanvasSize / (float)zoomArea.Height));
}
matrix = matrix.PreConcat(SKMatrix.CreateTranslation(-zoomArea.X, -zoomArea.Y));
canvas.SetMatrix(matrix);
DrawGrid(canvas);
}
and the grid-draw method
private void DrawGrid(SKCanvas canvas)
{
using (SKPaint paint = new SKPaint() { IsAntialias = true,Color=SKColors.LightGray,StrokeWidth = 1})
{
canvas.DrawLine(0, 0, 0, gridCanvas.Height, paint); //Size gridCanvas is always the same at the moment and defines the space where the grid is drawn
canvas.DrawLine(0, 0, gridCanvas.Width, 0, paint);
for (int i = 0; i <= (gridCanvas.Width - gridoffsetX) / pxPerSquare; i++)
{
canvas.DrawLine(i * pxPerSquare + gridoffsetX, 0, i * pxPerSquare + gridoffsetX, gridCanvas.Height, paint);
}
for (int i = 0; i <= (gridCanvas.Height - gridoffsetY) / pxPerSquare; i++)
{
canvas.DrawLine(0, i * pxPerSquare + gridoffsetY, gridCanvas.Width, i * pxPerSquare + gridoffsetY, paint);
}
}
}
and finally the original Paint Event
private void Pb1_PaintSurface(object sender, SKPaintGLSurfaceEventArgs e)
{
win2.UpdateDrawing(); //Just calls .Invalidate() on pb2
painter.Update(e, false);
}
examplePicture
So my question is: Is there a way to make both controls draw at nearly the same time without delay, although I don't understand why the first PictureBox draws in real time and the second doesn't...
Thanks!
after searching for day i found this page right after posting, which helped me:
Onpaint events (invalidated) changing execution order after a period normal operation (runtime)

How to create transparent controls in Windows Forms when controls are layered

I am trying to implement a "Fillable Form" in which editable text fields appear over top of an image of a pre-preprinted form for a dot matrix printer. (using c# and Windows Forms and targeting .Net 2.0) My first idea was to use the image as the Windows Form background, but it looked horrible when scrolling and also did not scroll properly with the content.
My next attempt was to create a fixed-size window with a panel that overflows the bounds of the window (for scrolling purposes.) I added a PictureBox to the panel, and added my textboxes on top of it. This works fine, except that TextBoxes do not support transparency, so I tried several methods to make the TextBoxes transparent. One approach was to use an odd background color and a transparency key. Another, described in the following links, was to create a derived class that allows transparency:
Transparency for windows forms textbox
TextBox with a Transparent Background
Neither method works, because as I have come to find out, "transparency" in Windows Forms just means that the background of the window is painted onto the control background. Since the PictureBox is positioned between the Window background and the TextBox, it gives the appearance that the TextBox is not transparent, but simply has a background color equal to the background color of the Window. With the transparency key approach, the entire application becomes transparent so that you can see Visual Studio in the background, which is not what I want. So now I am trying to implement a class that derives from TextBox and overrides either OnPaint or OnPaintBackground to paint the appropriate part of the PictureBox image onto the control background to give the illusion of transparency as described in the following link:
How to create a transparent control which works when on top of other controls?
First of all, I can't get it working (I have tried various things, and either get a completely black control, or just a standard label background), and second of all, I get intermittent ArgumentExceptions from the DrawToBitmap method that have the cryptic message "Additional information: targetBounds." Based on the following link from MSDN, I believe that this is because the bitmap is too large - in either event it seems inefficient to capture the whole form image here because I really just want a tiny piece of it.
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.drawtobitmap(v=vs.100).aspx
Here is my latest attempt. Can somebody please help me with the OnPaintBackground implementation or suggest a different approach? Thanks in advance!
public partial class TransparentTextbox : TextBox
{
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e); // not sure whether I need this
if (Parent != null)
{
foreach (Control c in Parent.Controls)
{
if (c.GetType() == typeof(PictureBox))
{
PictureBox formImg = (PictureBox)c;
Bitmap bitmap = new Bitmap(formImg.Width, formImg.Height);
formImg.DrawToBitmap(bitmap, formImg.Bounds);
e.Graphics.DrawImage(bitmap, -Left, -Top);
break;
}
}
Debug.WriteLine(Name + " didn't find the PictureBox.");
}
}
}
NOTE: This has been tagged as a duplicate, but I referenced the "duplicate question" in my original post, and explained why it was not working. That solution only works if the TextBox sits directly over the Window - if another control (such as my Panel and PictureBox) sit between the window and the TextBox, then .Net draws the Window background onto the TextBox background, effectively making its background look gray, not transparent.
I think I have finally gotten to the bottom of this. I added a Bitmap variable to my class, and when I instantiate the textboxes, I am setting it to contain just the portion of the form image that sits behind the control. Then I overload OnPaintBackground to display the Bitmap, and I overload OnPaint to manually draw the text string. Here is the updated version of my TransparentTextbox class:
public partial class TransparentTextbox : TextBox
{
public Bitmap BgBitmap { get; set; }
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, new PointF(0.0F, 0.0F));
}
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.DrawImage(BgBitmap, 0, 0);
}
}
... and here is the relevant part of how I instantiate:
Bitmap bgImage = (Bitmap)Bitmap.FromStream(Document.FormImage);
PictureBox pb = new PictureBox();
pb.Image = bgImage;
pb.Size = pb.Image.Size;
pb.Top = 0;
pb.Left = 0;
panel1.Controls.Add(pb);
foreach (FormField field in Document.FormFields)
{
TransparentTextbox tb = new TransparentTextbox();
tb.Width = (int)Math.Ceiling(field.MaxLineWidth * 96.0);
tb.Height = 22;
tb.Font = new Font("Courier", 12);
tb.BorderStyle = BorderStyle.None;
tb.Text = "Super Neat!";
tb.TextChanged += tb_TextChanged;
tb.Left = (int)Math.Ceiling(field.XValue * 96.0);
tb.Top = (int)Math.Ceiling(field.YValue * 96.0);
tb.Visible = true;
Bitmap b = new Bitmap(tb.Width, tb.Height);
using (Graphics g = Graphics.FromImage(b))
{
g.DrawImage(bgImage, new Rectangle(0, 0, b.Width, b.Height), tb.Bounds, GraphicsUnit.Pixel);
tb.BgBitmap = b;
}
panel1.Controls.Add(tb);
}
I still need to work on how the text looks when I highlight it, and other things like that, but I feel like I am on the right track. +1 to Reza Aghaei and Mangist for commenting with other viable solutions!

DrawImage() 'sprite' not showing up

I'm attempting to use a custom built .PNG file of letters from the alphabet to give my game some nice looking textual graphics. However, since the core functionality of my WinForms game works using minimal graphics (I have a timer set to fire a little animation routine that fades out the word "Guessed" when a user enters a word that they've already tried - this is done using DrawString()). Right now I'm trying to get DrawImage(Image, dstRect, srcRect, Unit) to work, and I have it set to run on form load.
private void DrawLetters()
{
// Create image.
Graphics letters = this.CreateGraphics();
Image newImage = Image.FromFile(strPath + "Letter_Map.png");
// Create rectangle for displaying image.
Rectangle destRect = new Rectangle(25, 25, 80, 80);
// Create rectangle for source image.
Rectangle srcRect = new Rectangle(0, 0, 833, 833);
GraphicsUnit units = GraphicsUnit.Pixel;
// Draw image to screen.
letters.DrawImage(newImage, destRect, srcRect, units);
}
This is practically verbatim from the MSDN site. My form populates with an array of Labels right now to show the user the grid of letters needed for the game. Is it them being drawn that overwrites the custom rectangle? I want to replace them all with images eventually. I understand srcRect to be (x, y, width, height) so I gave it the full size of the 'spritesheet'. For dstRect I only want it to place a block 80x80px on the form starting around 25x, 25y.
For the heck of it I created a blank form and called my DrawLetters() function during that form load event (I copied the function into that Form's code). I saw nothing, though, so I am starting to get a bit confused. I may need some education about just HOW the drawing works in conjunction with forms and controls being drawn on screen.
EDIT This https://stackoverflow.com/questions/837423/render-a-section-of-an-image-to-a-bitmap-c-sharp-winforms was what initially got me going, but this user has a working knowledge of XNA and seems to be trying to combine that with WinForms. XNA would be overkill, I believe, for the simple text game I am trying to 'pretty up'.
You need to override the forms OnPaintMethod to get access to the forms Graphics object which you can then use to display the image on the form.
If you want to display a portion of the image, you will need to use a different overload of DrawImage, as follows:
public partial class DrawImageDemo : Form
{
public DrawImageDemo()
{
InitializeComponent();
}
private Image _sprites;
public Image Sprites
{
get
{
if (_sprites == null)
{
_sprites = Image.FromFile("test.jpg");
}
return _sprites;
}
}
protected override void OnPaint(PaintEventArgs e)
{
// The forms graphics object
Graphics g = e.Graphics;
// Portion of original image to display
Rectangle region = new Rectangle(0, 0, 80, 80);
g.DrawImage(Sprites, 25, 25, region, GraphicsUnit.Pixel);
base.OnPaint(e);
}
}

printing quality winform

I have 2 problems while trying to print from a WinForms application. The first is a very very bad quality no matter what I try. The second is that I have a big page margin from the top left corner and the winform is cutting. Any ideas? This is my code:
Bitmap MemoryImage;
public void GetPrintArea(Panel pnl)
{
MemoryImage = new Bitmap(pnl.Width, pnl.Height);
Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height);
pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width, pnl.Height));
}
protected override void OnPaint(PaintEventArgs e)
{
if (MemoryImage != null)
{
e.Graphics.DrawImage(MemoryImage, 0, 0);
base.OnPaint(e);
}
}
void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
Rectangle pagearea = e.PageBounds;
e.Graphics.DrawImage(MemoryImage, (pagearea.Width / 2) - (this.panel1.Width / 2), this.panel1.Location.Y);
}
public void Print(Panel pnl)
{
panel1 = pnl;
GetPrintArea(pnl);
printPreviewDialog1.Document = printdoc1;
printPreviewDialog1.ShowDialog();
}
private void button2_Click(object sender, EventArgs e)
{
Print(this.panel1);
}
This comes up over and over again. There just is no magic solution, although eventually the problem is likely to disappear. The emergence of "retina" displays is pivotal.
The core issue is that monitors have a resolution that's drastically worse than printers. A typical printer has a resolution of 600 dots per inch. Which makes it capable of printing 6600 x 5100 individual pixels on a piece of paper. Much, much more than what a monitor can display, a full HD monitor tops out at 1920 x 1080 pixels. Roughly a factor of 5 worse, give or take.
This works out poorly when you print what shows up on a monitor on a piece of paper and try to keep it the same size. Inevitably, because of the lack of pixels on the monitor, each one pixel from the monitor is printed as a 5x5 blob on paper. If you try to keep the pixel mapping one-to-one, you will get a razor-sharp copy on paper. But it has turned into a postage-stamp.
Inevitably, the printout looks very grainy due those pixel blobs. What looks especially poor is text. Operating systems use lots of tricks to make text look good on monitors with poor resolution. Anti-aliasing is standard, tricks like ClearType were designed to take advantage of monitor physics that can help increase the perceived resolution. This no longer works when the text is printed, those anti-aliasing pixels turn into blobs and become very visible, completely ruining the effect. Especially bad for ClearType text on a color printer, the red and blue color fringes now can be clearly seen.
The only decent approach is to render to a printer using the actual resolution and not the monitor resolution. Like using the PrintDocument class in .NET. Using a report generator can help avoid having to write the code for it.
You should draw yourself on the Graphics object you get when the PrintDocument prints. That gives you all the control you need. Still everything Hans Passant said applies here as well...
Keep in mind that this is the simplest implementation that merely demo's what can be achieved, I'm not claiming that this is the easiest/best/most productive way... my code doesn't take multiple pages, controls contained in contaimers or controls not of the type Label and PictureBox.
I used the Draw... methods from System.Drawing.Graphics
sligthly adapted from the code above to get this working:
public void GetPrintArea(Panel pnl, Graphics gr)
{
// scale to fit on width of page...
if (pnl.Width > 0)
{
gr.PageScale = gr.VisibleClipBounds.Width/pnl.Width;
}
// this should recurse...
// just for demo so kept it simple
foreach (var ctl in pnl.Controls)
{
// for every control type
// come up with a way to Draw its
// contents
if (ctl is Label)
{
var lbl = (Label)ctl;
gr.DrawString(
lbl.Text,
lbl.Font,
new SolidBrush(lbl.ForeColor),
lbl.Location.X, // simple based on the position in the panel
lbl.Location.Y);
}
if (ctl is PictureBox)
{
var pic = (PictureBox)ctl;
gr.DrawImageUnscaledAndClipped(
pic.Image,
new Rectangle(
pic.Location.X,
pic.Location.Y,
pic.Width,
pic.Height));
}
}
}
void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
{
e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
e.Graphics.InterpolationMode =Drawing2D.InterpolationMode.HighQualityBilinear;
e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
GetPrintArea(panel1, e.Graphics);
}
You can actually print sharper controls by applying scaling to them on a "vector" level instead of bitmap level.
This snapshot shows the result of the following technique (please don't mind my Win2000-ish UI :-) ):
What we do is to iterate through the control's ControlCollection in a very similar fashion as rene shows in his answer.
But - in addition we apply scale to location, size and font to the control itself before we draw its bitmap to a preset size bitmap which in this case is 5 times bigger (4 times would represent about 300 DPI which is the effective print resolution on most printers).
The reason for this is to keep the thin line on the control sharp on print, or we could just scale the bitmap itself which wouldn't give us any benefit resolution-wise. By scaling font we reduce the anti-alias effect and can provide a better print quality.
To do so you can first in the button's click event setup the following:
//this will produce 5x "sharper" print
MemoryImage = new Bitmap((Panel1.Width * 5), (Panel1.Height * 5));
Using Graphics g = Graphics.FromImage(MemoryImage) {
ScaleControls(Panel1, g, 5);
};
PrintPreviewDialog1.Document = printdoc1;
PrintPreviewDialog1.ShowDialog();
Now in the ScaleControls function, which is recursive, we scale location, size and font in order to make each control itself in higher resolution before we draw them to the bitmap:
private void ScaleControls(Control c, ref Graphics g, double s)
{
//To detach controls for panels, groupboxes etc.
List<Control> hold = null;
foreach (Control ctrl in c.Controls) {
if (ctrl is GroupBox || ctrl is Panel) {
//backup reference to controls
hold = new List<Control>();
foreach (Control gctrl in ctrl.Controls) {
hold.Add(gctrl);
}
ctrl.Controls.Clear();
}
//backup old location, size and font (see explanation)
Point oldLoc = ctrl.Location;
Size oldSize = ctrl.Size;
Font oldFont = ctrl.Font;
//calc scaled location, size and font
ctrl.Location = new Point(ctrl.Location.X * s, ctrl.Location.Y * s);
ctrl.Size = new Size(ctrl.Size.Width * s, ctrl.Height * s);
ctrl.Font = new Font(ctrl.Font.FontFamily, ctrl.Font.Size * 5,
ctrl.Font.Style, ctrl.Font.Unit);
//draw this scaled control to hi-res bitmap
using (Bitmap bmp = new Bitmap(ctrl.Size.Width, ctrl.Size.Height)) {
ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle);
g.DrawImage(bmp, ctrl.Location);
}
//restore control's geo
ctrl.Location = oldLoc;
ctrl.Size = oldSize;
ctrl.Font = oldFont;
//recursive for panel, groupbox and other controls
if (ctrl is GroupBox || ctrl is Panel) {
foreach (Control gctrl in hold) {
ctrl.Controls.Add(gctrl);
}
ScaleControls(ctrl, g, s);
}
}
}
and finally in the event handler for printing:
double scale = MemoryImage.Width / e.PageBounds.Width;
e.Graphics.DrawImage(MemoryImage, 0, 0,
Convert.ToInt32(MemoryImage.Width / scale),
Convert.ToInt32(MemoryImage.Height / scale));
Now, in this example we scale the controls in-place. This is not ideal of course as they will appear to live their own life while we do a print preview.
Ideally we would clone each control as we iterated and discard it after we drew to bitmap. This would also eliminate the need to backup geometries. But for the example of principle I left it as-is. I'll leave it to you to clone etc.
The reason for detaching controls is that if we don't (as the code is now - this can surely be changed by providing another method of iterating, ie. pre-scale cloned controls) the non-scaled in the f.ex. GroupBox control will be printed first, then the scaled ones will show on top of those when they are iterated. This because we DrawToBitmap the GroupBox before scaling its controls. This is something I'll leave for you to handle.
The bitmap we're working on will not necessary fit the resolution of the print that the user end up with from setting up the print dialog, but we get a higher resolution to work with which in turn yield a better result than the poor screen bitmap resolution we have initially.
You would of course need to add support for special cases for other controls than Panel and GroupBox that can hold other controls, image controls and so forth.
I spent days looking for a way to print a panel and its contents in high quality. This didnt work, and I tried other peoples codes and they were all either wrong or just bad quality, until I found this:
http://rkinfopedia.blogspot.com/2008/07/printing-contents-of-panel-control.html
just put the eventhandler inside the print button click event handler, and include the print method in it too, like this:
private void button3_Click(object sender, EventArgs e)
{
printdoc1.PrintPage += new PrintPageEventHandler(printdoc1_PrintPage);
Print(panel1);
}
and place an if statement inside the override OnPaint method, like this:
protected override void OnPaint(PaintEventArgs e)
{
if (MemoryImage != null)
{
e.Graphics.DrawImage(MemoryImage, 0, 0);
base.OnPaint(e);
}
}
rest is fine as is, and youll finally get almost perfect print quality
Just wanted to share this gem, you are welcome internet stranger!
Thank you Mr. Rakesh!

C# Drawing - best solution

Today I am trying to solve problem with a blinking panel, when I draw onto it.
Lots of threads I read, like these:
how to stop flickering C# winforms,
Double buffering with Panel,
How can I draw on Panel so it does not blink?
So I tried to draw onto PictureBox, MyPanel with doubleBuffered, but the best solution I found, when I read, that I can't use g.Clear() every time, after that, even on non-doubleBuffered panel, blinking disappeared.
I even read, that I should free Graphics after draw is done. So I use everywhere using(Graphics g = panel.CreateGraphics()).
So my question, is it a great idea to create graphics for bitmap only when I draw something to it? Because before I created Bitmap, and Graphics (only for this bitmap, not for all components), so I had Graphics available for this bitmap every time
Here is my code:
public void newSizeDrawing()
{
Size size = collector.getLetterSize(selectedName);
Size drawingSize = new Size(size.Width * (pixelSizeArray[pixelSize] + 1),size.Height * (pixelSizeArray[pixelSize] + 1));
bitmapDraw = new Bitmap(drawingSize.Width, drawingSize.Height);
int width = (this.MinimumSize.Width - panelDraw.MinimumSize.Width) + drawingSize.Width + 10;
int height = (this.MinimumSize.Height - panelDraw.MinimumSize.Height) + drawingSize.Height + 10;
this.Size = new Size(
(width > this.MinimumSize.Width) ? width : this.MinimumSize.Width,
(height > this.MinimumSize.Height) ? height : this.MinimumSize.Height);
zeroDrawPosition = new Point((panelDraw.Size.Width - bitmapDraw.Width) / 2 - 1, (panelDraw.Size.Height - bitmapDraw.Height) / 2 - 1);
using (Graphics g = panelDraw.CreateGraphics())
{
g.Clear(panelDraw.BackColor);
}
redrawDrawingLetter();
}
public void redrawDrawingLetter()
{
bool[][] grid = collector.getArray(selectedName);
using (Graphics graphicDraw = Graphics.FromImage(bitmapDraw))
{
graphicDraw.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
graphicDraw.Clear(panelDraw.BackColor);
int pxSize = pixelSizeArray[pixelSize];
for (int y = 0; y < grid.Length; y++)
{
for (int x = 0; x < grid[y].Length; x++)
{
graphicDraw.FillRectangle((grid[y][x] ? Brushes.Black : Brushes.White), x * (pxSize + 1), y * (pxSize + 1), pxSize, pxSize);
}
}
}
redrawDrawingPanel();
}
private void redrawDrawingPanel()
{
using (Graphics g = panelDraw.CreateGraphics())
{
if (bitmapDraw != null)
g.DrawImage(bitmapDraw, zeroDrawPosition);
}
}
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
Nobody can explain to me how to draw in C# the best way. So maybe my code isn't good, but that is reason why I asking how to do it correctly.
newSizeDrawing is called by myself only, when user click on + or - button. I have bool double-dimension array if pixel is on or off. This is program for drawing letters for microchips and LED display (often 8px height of letter).
I wrote a method that checks if the mouse moved from one "pixel" to another, so I don't redraw it after every call mouseMove event, because "pixel" can be from 10x10 px to 30x30 px.
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
This is fundamentally wrong. The Paint event passes e.Graphics to let you draw whatever you want to paint. When you turn on double-buffering, e.Graphics refers to a bitmap, it is initialized with the BackColor. You then proceed to drawing using another Graphics object you got from CreateGraphics(). That one draws directly to the screen.
The flicker effect you see if very pronounced. For a split second you see what the other Graphics context draws. Then your panelDraw_Paint() method returns and Winforms draws the double-buffered bitmap. There's nothing on it so it immediately erases what you drew.
Modify the redrawDrawingPanel() method and give it an argument of type Graphics. Pass e.Graphics in the call. And only use that Graphics object, remove all calls to CreateGraphics().

Categories