Why is my screen flickering even though I have double buffering on? - c#

Ok, Im pretty new to c# graphics and I'm trying to make a top down adventure game type thing. The problem is anything displayed above the background flickers. Everything is a bitmap from a png file. The background doesnt flicker so I dont know where I'm going wrong.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AITS
{
public partial class Form1 : Form
{
Background background;
Foreground foreground;
Character player;
Graphics g;
public Form1()
{
InitializeComponent();
Console.WriteLine(Environment.CurrentDirectory);
background = new Background(Properties.Resources.background, Width, Height);
foreground = new Foreground(100, Width);
player = new Character();
DoubleBuffered = true;
Paint += DrawScreen;
KeyDown += KeyPressed;
Shown += Form1_Shown;
g = CreateGraphics();
}
private void Form1_Shown(Object sender, EventArgs e)
{
gameLoop();
}
private void DrawScreen(object sender, PaintEventArgs args)
{
background.Draw(g);
player.Draw(g);
foreground.Update(Height, Width);
foreground.Draw(g);
}
private void KeyPressed(object sender, KeyEventArgs e)
{
Console.WriteLine(e.KeyData.ToString());
}
public void gameLoop()
{
while (this.Created)
{
Invalidate();
Refresh();
Application.DoEvents();
}
}
}
}
EDIT:
Ok i found the answer, for anyone who couldnt find this like me:
g should = args.Graphics. DO NOT use CreateGraphics()!

The screen flickers, because the form first redraws its background before letting you paint on it.
You can suspend this behavior by overriding WndProc:
private const int WM_ERASEBKGND = 20;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_ERASEBKGND)
{
m.Result = IntPtr.Zero;
}
else
{
base.WndProc(ref m);
}
}

Ok i found the answer, for anyone who couldnt find this like me: g should = args.Graphics. DO NOT use CreateGraphics()!

Related

Draw graphics on click and make them stay

This little program opens a windows form and draws 70 red rectangles, where the user clicks on the form.
Every time the user clicks, the rectangles disappear, and new ones are drawn on the new click-Point.
I want to make the rectangles to stay when the user clicks and draws a new set of rectangles.
How do i do that?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace tegnRektangel
{
public partial class Form1 : Form
{
int x;
int y;
bool mouseClicked = false;
Graphics g = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
}
private void Form1_Resize(object sender, EventArgs e)
{
Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (mouseClicked)
{
g = panel1.CreateGraphics();
paintRectangel();
}
}
private void paintRectangel()
{
for (int i = 1; i <= 70; i++)
{
g.DrawRectangle(Pens.Red, x - 50-i*5, y - 40-i*5, 100, 80);
}
g.Dispose();
}//end paint
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
mouseClicked = true;
Point clickPoint = new Point(e.X,e.Y);
x = clickPoint.X;
y = clickPoint.Y;
panel1.Invalidate();
}
}
}
From MSDN:
The Graphics object that you retrieve through the CreateGraphics
method should not normally be retained after the current Windows
message has been processed, because anything painted with that object
will be erased with the next WM_PAINT message.
You can work around it like this:
In the click event, add the (x, y) coordinate to a list of coordinates.
In the paint event, iterate all these (x, y) coordinates and paint each rectangle.
Here is some code to demonstrate creating rectangles for each click, storing them, and painting all stored rectangles.
public partial class Form1 : Form
{
private List<Rectangle> Rectangles { get; set; }
public Form1()
{
InitializeComponent();
Rectangles = new List<Rectangle>();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (Rectangles.Count > 0)
e.Graphics.DrawRectangles(Pens.Red, Rectangles.ToArray());
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
for (int i = 1; i <= 70; i++)
{
Rectangles.Add(new Rectangle(e.X - 50 - i * 5, e.Y - 40 - i * 5, 100, 80));
}
Invalidate();
}
}

How do you make an object be limited to the boundaries of a Form

I am currently making a Space Invaders type game using C# Windows Forms. When creating the laser cannon which the user controls, I want them to move left and right, shooting lasers. This is my current code for the movement so far:
enter code here
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Move
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_KeyDown(object sender, KeyEventArgs e)
{
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
int i;
for (i = 0; i < 500; i++)
{
if (e.KeyCode == Keys.Up)
{
pictureBox1.Location = new Point(pictureBox1.Left - 1);
Application.DoEvents();
System.Threading.Thread.Sleep(10);
}
if (e.KeyCode == Keys.Down)
{
pictureBox1.Location = new Point(pictureBox1.Left + 1);
Application.DoEvents();
System.Threading.Thread.Sleep(10);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
However, I'm having an issue with this; if the image goes too far, it goes out of the boundaries of the form but I want it to do is bounce off and go the other way.
I tried doing this but to no avail, it only moves by a pixel but I think it's a good step(right?...):
if(pictureBox1.Location == new Point(300,300))
{
pictureBox1.Location = new Point(pictureBox1.Left - 1);
}
Like how do I get the correct X,Y coordinates of the whole form so I could tell it to go the other way as well as actually getting it to move?
The smallest X and Y position for a picture box to stay inside the form is 0.
The largest X a picture box can go to is Form.ClientSize.Width - pictureBox.Size.Width
The largest Y a picture box can go is Form.ClientSize.Height - pictureBox.Size.Height.
Check all of these things in an if statement. If either the X or Y of the picture box is outside of the ranges, don't move the picture box, otherwise, do move it.

scrolling issues while dealing with graphics in c# .net

I have a panel and a richTextBox placed at the bottom corner of the panel. Panel takes text from richTextBox and Draws using DrawString() on the panel using its paint event.
When the panel is filled it does not auto-scroll the panel even if autoscroll=true.
How to enable scrollings to a paint event handling panel?
How to reflect the scrolling of richTexBox in a panel(if richtextbox is scrolled to some extent, the panel should also be scrolled to the same extent?)
In short the visible content of richtextbox must be visible in the panel exactly.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
namespace TextFormatter
{
public partial class Form1 : Form
{
//int scrol;
public Form1()
{
InitializeComponent();
//Set Double Buffering
panel1.GetType().GetMethod("SetStyle",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic)
.Invoke(panel1, new object[] {
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.DoubleBuffer, true });
}
class myFont
{
public int size;
public string family;
public Color c;
}
myFont f = new myFont();
private void Form1_Load(object sender, EventArgs e)
{
}
private void UpdateStatus()
{
panel1.Refresh();
}
delegate void UpdateStatusInvoker();
private void panel1_Paint_1(object sender, PaintEventArgs e)
{
Point P = new Point();
int w = this.ClientSize.Width;
int h = this.ClientSize.Height;
P.X = w - 180;
P.Y = h - 52;
richTextBox1.Location = (P);
Graphics g = e.Graphics;
Brush b;
b = new SolidBrush(f.c);
if (f.c.IsEmpty)
{
b = new SolidBrush(Color.Brown);
}
Point p = new Point();
p.X = 100;
p.Y = 100;
Font ff = new Font(f.family, f.size | 20);
g.DrawString("" + richTextBox1.Text, ff, b, p);
// richTextBox1.AppendText(
b.Dispose();
// g.Dispose();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
// Invalidate();
this.Invoke(new UpdateStatusInvoker(UpdateStatus));
}
private void richTextBox1_VScroll(object sender, EventArgs e)
{
// same scrolling must be applied on panel.
}
private void panel1_Scroll(object sender, ScrollEventArgs e)
{
// control the scrolling of graphical content of the panel
}
}
}

Mousewheel scroll down event in C# winform programmatically done

I am trying do image slide show with picturebox Control and a trackbar. The trackbar gets minimum and maximum value corresponds to the number of images to show. I use a timer to get interval period for the slide along with trackbar valuechange.
Now, here is the main thing for each image in the picturebox I'm drawing a rectangle box over the image.
I am not able to draw on the first image when the form get load with first image. But I could do if I scroll the mouse wheel.
I need help to trigger the mouse wheel scroll event once after the first image get loaded.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace test
{
public partial class Form1 : Form
{
public event MouseEventHandler MouseWheel;
//MouseEventArgs k = new MouseEventArgs(MouseButtons.Middle,0,0,-1);
string[] pics = { "C:\\Downloads\\folder_picture_green.png", "C:\\Downloads\\Aetherpal.ico", "C:\\Downloads\\folder_picture_green.png" };
public Form1()
{
InitializeComponent();
this.trackBar1.Minimum = 0;
this.trackBar1.Maximum = (pics.Count() - 1);
//this.trackBar1.Maximum = 0;
imageupdate(0);
timer1.Start();
timer1.Interval = 3000;
this.MouseWheel += test;
this.MouseWheel(null, null);
}
private void test(object sender, System.Windows.Forms.MouseEventArgs e)
{
MessageBox.Show("Scrolled");
}
private void check(object sender, System.EventArgs e)
{
//if (Initializing == false) { return; }
if(this.trackBar1.Value < this.trackBar1.Maximum )
this.trackBar1.Value += 1;
else
timer1.Stop();
}
private void Valuechange(object sender, System.EventArgs e)
{
imageupdate(this.trackBar1.Value);
if(this.trackBar1.Value < this.trackBar1.Maximum)
timer1.Start();
}
private void imageupdate(int k)
{
this.pictureBox1.Refresh();
this.pictureBox1.Image = new Bitmap(pics[k]);
Pen blackPen = new Pen(Color.Blue, 5);
this.pictureBox1.Refresh();
using (Graphics g = this.pictureBox1.CreateGraphics())
{
g.DrawRectangle(blackPen, 10, 10, 100, 50);
}
}
}
}
You can add this code to your form to scroll your form (of course with MouseWheel):
private void Wheel(int ticks, bool down){
//WM_MOUSEWHEEL = 0x20a
Message msg = Message.Create(Handle, 0x20a, new IntPtr((down ? -1 : 1)<<16), new IntPtr(MousePosition.X + MousePosition.Y << 16));
for(int i = 0; i < ticks; i++)
WndProc(ref msg);
}
//Use it
Wheel(120,true);//Wheel down
Wheel(120,false);//Wheel up
NOTE: I can see you define a MouseWheel event in your own form. This will hide the base MouseWheel event, I don't think you have any reason to do this, your own MouseWheel can't work as the base one, you have to catch the win32 message and raise it yourself, we should use the base MouseWheel event instead. (Maybe you thought there isn't any MouseWheel event in your form class?)

C# bitmap drawing doesn't render to the screen

I am trying to write a drawing program for use with a tablet. For this I need fall-off and transparency for use with pressures. So I am using the bitmap system in C# for image construction.
I cannot seem to get my drawing code at the moment to display anything. It is being rendered to a picture box. I know there is some stuff being input to the bitmap as it shows up when I do a bitmap save.
I have had a look around an pretty much all C# drawing questions refer to using the line drawing or ellipse drawing stuff as opposed to bitmaps
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace paint1
{
public partial class Form2 : Form
{
public Bitmap m_bitmap;
public bool m_penDown;
public int m_lastX;
public int m_lastY;
public int m_currentX;
public int m_currentY;
public Form2()
{
InitializeComponent();
// Create the bitmap area
m_bitmap = new Bitmap(this.Width, this.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
m_penDown = false;
Graphics m_graphics = Graphics.FromImage(m_bitmap);
m_lastX = System.Windows.Forms.Cursor.Position.X;
m_lastY = System.Windows.Forms.Cursor.Position.Y;
m_currentX = System.Windows.Forms.Cursor.Position.X;
m_currentY = System.Windows.Forms.Cursor.Position.Y;
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void Form2_Paint(object sender, PaintEventArgs e)
{
Graphics objGraphics;
//You can't modify e.Graphics directly.
objGraphics = e.Graphics;
// Draw the contents of the bitmap on the form.
objGraphics.DrawImage(m_bitmap, 0, 0,
m_bitmap.Width,
m_bitmap.Height);
objGraphics.Dispose();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
m_penDown = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
m_penDown = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
m_lastX = m_currentX;
m_lastY = m_currentY;
m_currentX = System.Windows.Forms.Cursor.Position.X;
m_currentY = System.Windows.Forms.Cursor.Position.Y;
if(m_penDown)
m_bitmap.SetPixel(m_currentX, m_currentY, Color.Gray);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Form2_Paint(sender, e);
this.pictureBox1.Image = m_bitmap;
}
private void Form2_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
m_bitmap.Save(#"C:\Users\rpettefar\Documents\My Dropbox\Programming\paint1\preview.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
}
}
}
}
I am a bit new to c# so I am very open to any other issues or things that may come to your attention too.
You will have to assign your bitmap to the picture box.
myPictureBox.Image = m_bitmap;
You can do that after you changed the bitmap or assign it once and then invalidate your PictureBox.
myPictureBox.Invalidate();
This tells your form to refresh the picture on the screen. There is no need to override OnPaint. Draw to the bitmap using the Graphics object you created in the constructor of the form (if you want to make more complicated things than just drawing single pixels). The PictureBox will do the rest.
It looks like there's at least two ways you're trying to get the image on screen; can't say immediately what's wrong, but I would say definitely get rid of that objGraphics.Dispose(); line - you didn't create the Graphics (you were passed it), so you shouldn't Dispose it.
I cleaned up your code a bit. You probably shouldn't use a picturebox for this.
Here is a form with just a panel:
public partial class Form1 : Form
{
public Bitmap m_bitmap;
public Point m_lastPoint = Point.Empty;
public Form1()
{
InitializeComponent();
m_bitmap = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(m_bitmap))
g.Clear(SystemColors.Window);
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(m_bitmap, new Point(0, 0));
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
m_lastPoint = e.Location;
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
using (Graphics g = Graphics.FromImage(m_bitmap))
g.DrawLine(Pens.Black, m_lastPoint, e.Location);
m_lastPoint = e.Location;
panel1.Invalidate();
}
}
}
The other posters have largely answered the question, but in my experience, I'd add that you'll likely get some flicker with this method. If you do, one thing you can do to help with this is sub-class your rendering target and enable double buffering. For a picture box, it would look something like this:
public class DoubleBufferedPictureBox : PictureBox
{
/// <summary>
/// Creates an instance of the DoubleBufferedPictureBox.
/// </summary>
public DoubleBufferedPictureBox() : base()
{
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true);
}
}

Categories

Resources