I have data in a multidimensional array of doubles and I want to draw it on my form in a grid (with different values being different colors). Write now I am using a Panel control and in its OnPaint() I do do the drawing by calling Graphics.DrawRectangle() for each entry in the array. The data changes very frequently, so I call Refresh() on the panel when it does, but this is quite slow and I get a lot of flickering, I can also see the drawing of each rectangle happen sequentially, and I know that parallel drawing of the rectangles is not possible, because it is not thread-safe.
Would constructing a Bitmap where each pixel is an entry in the array, and then setting a PictureBox to show the Bitmap (and scale its size up) be faster than using the panel and OnPaint?
Here is a sample of the code I am using:
namespace PredPreySim
{
public partial class SimulationForm : Form
{
Simulation simulation; // instance of the simulation class
bool running = true;
int simWidth = 30;
int simHeight = 20;
int cellSize = 15;
System.Threading.Thread t;
public SimulationForm()
{
InitializeComponent();
simulation = new Simulation(simWidth, simHeight, numHerbivores, 0.3);
graphicsTimeDelay = Convert.ToInt32(GraphicsTimeDelay.Value);
ResizePanel();
}
private void ResizePanel()
{
panel1.Width = simWidth * cellSize;
panel1.Height = simHeight * cellSize;
}
// draws the board
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics;
SolidBrush brush = new SolidBrush(Color.Green);
RectangleF rectangle = new Rectangle();
rectangle.Width = cellSize;
rectangle.Height = cellSize;
// draw all the plants
Color plantColor;
for (int i = 0; i < simWidth; ++i)
{
for (int j = 0; j < simHeight; ++j)
{
int r, g = 255, b;
r = b = 255 - (int)(255.0 * Math.Tanh(simulation.plants[i, j]));
plantColor = Color.FromArgb(100, r, g, b);
brush.Color = plantColor;
rectangle.Location = new PointF(i * cellSize, j * cellSize);
graphics.FillRectangle(brush, rectangle);
}
}
brush.Dispose();
graphics.Dispose();
}
private void StartStopButton_Click(object sender, EventArgs e)
{
if (running) // then stop
{
running = false;
StartStopButton.Text = "Start";
}
else // start
{
running = true;
StartStopButton.Text = "Stop";
t = new Thread(new ThreadStart(this.runSimulation));
t.Start();
t.Priority = System.Threading.ThreadPriority.Highest;
}
}
private void runSimulation()
{
while (running)
{
simulation.Update();
panel1.Invoke(new MethodInvoker(Refresh)); // makes the call thread-safe
}
t.Abort();
}
}
}
I'm following a tutorial to get the basics of dealing with a paint event using windows forms.
So far the program kind of works, but any update of the graphics is not deleting the previously drawn lines (the graphics is not being disposed of).
The original tutorial used Refresh, but that didn't seem to work and I replaced it with Invalidate+Update.
Also, setting the graphics control to this.CreateGraphics() wasn't working and I switched it to panel2.CreateGraphics() (I also tried e.Graphics without results).
namespace GraphicsTutorialV1
{
public partial class Form1 : Form
{
Pen myPen = new Pen(Color.Black);
Graphics g = null;
static int start_x, start_y;
static int end_x, end_y;
static int my_angle = 0;
static int my_length = 0;
static int my_increment = 0;
static int num_lines = 0;
public Form1()
{
InitializeComponent();
Int32.TryParse(textBox1.Text, out num_lines);
Int32.TryParse(textBox2.Text, out my_angle);
Int32.TryParse(textBox3.Text, out my_length);
Int32.TryParse(textBox4.Text, out my_increment);
start_x = (panel2.Width / 2);
start_y = (panel2.Height / 2);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
myPen.Width = 1;
g = panel2.CreateGraphics();
//g = e.Graphics;
for(int i = 0; i < num_lines; i++)
{
drawLine();
}
}
private void drawLine()
{
int temp;
Int32.TryParse(textBox2.Text, out temp);
my_angle = my_angle + temp;
Int32.TryParse(textBox4.Text, out temp);
my_length = my_length + temp;
end_x = (int)(start_x + Math.Cos(my_angle * Math.PI / 180) * my_length);
end_y = (int)(start_y + Math.Sin(my_angle * Math.PI / 180) * my_length);
Point[] points =
{
new Point(start_x, start_y),
new Point(end_x, end_y)
};
start_x = end_x;
start_y = end_y;
g.DrawLines(myPen, points);
}
private void button1_Click(object sender, EventArgs e)
{
Int32.TryParse(textBox1.Text, out num_lines);
Int32.TryParse(textBox2.Text, out my_angle);
Int32.TryParse(textBox3.Text, out my_length);
Int32.TryParse(textBox4.Text, out my_increment);
this.Invalidate();
this.Update();
}
}
}
The problem with my code was that the drawing instructions were included in the paint event for the form. By setting the drawing in the paint event for the panel and then setting the graphics to the standard paint event for it everything worked out. Also, Refresh started to work.
I have a panel in Visual Studio/windows form app.But I coulnd't add pictureBox on it with code.It is working if I work without panel however I need it.MyCodes:
PictureBox[] pipe = new PictureBox[3];
private void Form1_Load(object sender, EventArgs e)
{
CreatePipes(1);}
private void CreatetopPipes(int Number)
{
for (int i = 0; i <= Number; i++)
{
PictureBox temp = new PictureBox();
this.Controls.Add(temp);
temp.Width = 50;
temp.Height = 350;
temp.BorderStyle = BorderStyle.FixedSingle;
temp.BackColor = Color.Red;
temp.Top = 30;
temp.Left = 300;
topPipe[i] = temp;
topPipe[i].Visible = true;
}
}
You can use panelName.Controls.Add(temp), and i advise you to change a top of PictureBox in for loop to view all PictureBox, like this :
private void CreatetopPipes(int Number)
{
for (int i = 0; i <= Number; i++)
{
PictureBox temp = new PictureBox();
panelName.Controls.Add(temp);
temp.Width = 50;
temp.Height = 350;
temp.BorderStyle = BorderStyle.FixedSingle;
temp.BackColor = Color.Red;
temp.Top = temp.Height * panelName.Controls.Count;
temp.Left = 300;
topPipe[i] = temp;
topPipe[i].Visible = true;
}
}
Graphics graphics = Graphics.FromImage(BitMap_1); // <--This is not allowed? How can I fix it?How can I declare graphics as global?
Here is some more code...
Here is some more code...
Here is some more code...
Here is some more code...
public partial class Form1 : Form
{
string WAV_filePath = #"";
string MIDI_filePath = #"";
string filePath = #"C:\Users\Steffan\Desktop\guitar\Gert toets die Elektroniese Konsertina.wav";//Guitar 2.wav";//Sine Wave 440Hz.wav";
SoundPlayer player1 = new SoundPlayer();
byte[] RawWaveDataArray = new byte[100];
Int16[] Data_16Bit = new Int16[100];
int NumberOfSamples = 0;
bool PLAY_ = false;
Point[] Points = new Point[886];
// Create pen.
Pen Pen_ = new Pen(Color.White, 0);
Bitmap BitMap_1 = new Bitmap(1138, 72);
Graphics graphics = Graphics.FromImage(BitMap_1); // <--This is not allowed? How can I fix it?How can I declare graphics as global?
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (PLAY_ == true)
{
Wave_x_Inc = 0;
graphics.Clear(Color.Black);
for (int x = 0; x < 886; x++)
{
Points[x] = new Point(Wave_x_Inc, (int)((Data_16Bit[DataIndex] * 0.002F) + (pictureBox4.Height / 2)));
Wave_x_Inc = Wave_x_Inc + 2;
DataIndex = DataIndex + 2;
if (DataIndex > Data_16Bit.Length/2) { DataIndex = 0; x = 886; }
}
if (Wave_x_Inc > pictureBox4.Width) { Wave_x_Inc = 0; }
graphics.DrawBeziers(Pen_, Points);
WaveLengthCounter = WaveLengthCounter + 886;
int Temp_Val = WaveLengthCounter / DataLenth_Fraction;
if (Temp_Val <= 300) { trackBar1.Value = Temp_Val; }
else { trackBar1.Value = 0; }
if (WaveLengthCounter > Data_16Bit.Length/2)
{
WaveLengthCounter = 0;
}
pictureBox4.Image = BitMap_1;
}
}
}
Try this, it may help you.
var bitmap = new Bitmap(width, height);
var graphics = Graphics.FromImage(bitmap);
graphics.DrawRectangle(Pens.Black, 0, 0, 10, 10);
bitmap.Save("MyShapes.png");
I need help with the Check button. After a user adds all the 42 numbers in the textbox and enter a number from 0-9 in the "Enter number" area and clicks on the start button, next what he should do is run through the array of labels with the red label or lblCovece and he should collect the same values like the number enters before. And after he clicks on the Check button, the programme should first validate if the value that is selected with the red label is the same as the number entered. If is valid the label should turn green and than the result should appear in the label lblResultE(the result for example should be like this: if the number entered is 2, the result it is 2+2+2...)and if is not valid in the lblResultE we take out 10 points. That's what i did by now with some help.:) thank you.
namespace Seminarska
{
public partial class Form1 : Form
{
private Label l,l2,lblCovece,l4,lblResultE;
private Button bUp, bRight, bLeft, bDown, bCheck,bStart, bReset;
private TextBox txtVnes, txtGoal;
private Label[] pole;
public Form1()
{
InitializeComponent();
l2 = new Label();
l2.Text = " Enter one number";
l2.Location = new Point(230, 200);
l2.AutoSize = true;
l4 = new Label();
l4.Text = "Score";
l4.Location = new Point(240, 260);
l4.AutoSize = true;
lblResultE = new Label();
lblResultE.Location = new Point(350, 260);
lblResultE.AutoSize = true;
bLeft = new Button();
bLeft.Location = new Point(0, 250);
bLeft.Width=75;
bLeft.Height = 25;
bLeft.Text = "LEFT";
bCheck = new Button();
bCheck.Location = new Point(75, 250);
bCheck.Width = 75;
bCheck.Height = 25;
bCheck.Text = "Check";
bRight = new Button();
bRight.Location = new Point(150, 250);
bRight.Width = 75;
bRight.Height = 25;
bRight.Text = "RIGHT";
bUp = new Button();
bUp.Location = new Point(75, 220);
bUp.Width = 75;
bUp.Height = 25;
bUp.Text = "UP";
bDown = new Button();
bDown.Location = new Point(75, 280);
bDown.Width = 75;
bDown.Height = 25;
bDown.Text = "DOWN";
bStart = new Button();
bStart.Location = new Point(240, 165);
bStart.Width = 75;
bStart.Height = 25;
bStart.Text = "START";
bReset = new Button();
bReset.Location = new Point(320, 165);
bReset.Width = 75;
bReset.Height = 25;
bReset.Text = "RESET";
txtVnes = new TextBox();
txtVnes.Location = new Point(240, 10);
txtVnes.Width = 160;
txtVnes.Height = 130;
txtVnes.Multiline = true;
txtGoal = new TextBox();
txtGoal.Width = 75;
txtGoal.Height = 25;
txtGoal.Location = new Point(330, 200);
lblCovece = new Label();
lblCovece.Location = new Point(160,165);
lblCovece.Width = 20;
lblCovece.Height = 20;
lblCovece.TextAlign = ContentAlignment.MiddleCenter;
lblCovece.Text = "O";
lblCovece.BackColor = Color.FromArgb(255, 0, 0);
int a = 0;
pole = new Label[42];
this.Controls.Add(lblCovece);
for (int i = 1; i <= 6; i++)
{
for (int j = 1; j <= 7; j++)
{
l = new Label();
l.Name = "label" + i.ToString() + j.ToString();
l.Text = "Z";
l.Width = 20;
l.Height = 20;
l.TextAlign = ContentAlignment.MiddleCenter;
l.Parent = this;
l.BackColor = Color.FromArgb(100, 149, 237);
l.Location = new Point(10 + (j - 1) * 25, 15 + (i - 1) * 25);
pole[a] = l;
this.Controls.Add(l);
a++;
}
}
this.Width = 460;
this.Height = 380;
this.Controls.Add(l2);
this.Controls.Add(l4);
this.Controls.Add(lblResultE);
this.Controls.Add(lblTimeE);
this.Controls.Add(bStart);
this.Controls.Add(bReset);
this.Controls.Add(txtGoal);
this.Controls.Add(txtVnes);
this.Controls.Add(bUp);
this.Controls.Add(bLeft);
this.Controls.Add(bRight);
this.Controls.Add(bDown);
this.Controls.Add(bCheck);
bStart.Click+=new EventHandler(bStart_Click);
bUp.Click+=new EventHandler(bUp_Click);
bDown.Click+=new EventHandler(bDown_Click);
bLeft.Click+=new EventHandler(bLeft_Click);
bRight.Click+=new EventHandler(bRight_Click);
bCheck.Click+=new EventHandler(bZemaj_Click);
bReset.Click+=new EventHandler(bReset_Click);
}
private void bStart_Click(object sender, EventArgs e)
{
string Str = txtGoal.Text.Trim();
int Num;
bool isNum = int.TryParse(Str, out Num);
if (isNum && Str.Length == 1)
{
string[] ts = txtVnes.Text.Split(
new string[] { "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
int row = 0;
for (int i = 0; i < ts.Length && row < 6; i++)
{
if (LineIsValid(ts[i]))
{
for (int col = 0; col < 7; col++)
{
pole[row * 7 + col].Text = ts[i][2 * col].ToString();
}
row++;
}
}
for (; row < 6; row++)
{
for (int col = 0; col < 7; col++)
{
pole[row * 7 + col].Text = "Z";
}
}
}
else
{
MessageBox.Show("Invalid Input");
}
}
private static Regex regex = new Regex(#"^(\s)*(\d ){6}\d(\s)*$");
private static bool LineIsValid(string line)
{
return regex.IsMatch(line);
}
private void bReset_Click(object sender, EventArgs e)
{
txtVnes.Clear();
string[] ts = txtVnes.Text.Split(new string[] { "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
int row = 0;
for (int i = 0; i < ts.Length && row < 6; i++)
{
for (int col = 0; col < 7; col++)
{
pole[row * 7 + col].Text = "Z";
pole[row * 7 + col].BackColor=Color.FromArgb(100, 149, 237);
}
row++;
}
for (; row < 6; row++)
{
for (int col = 0; col < 7; col++)
{
pole[row * 7 + col].Text = "Z";
pole[row * 7 + col].BackColor = Color.FromArgb(100, 149, 237);
}
}
txtGoal.Clear();
lblCovece.Location=new Point(160,165);
}
private void bUp_Click(object sender, EventArgs e)
{
lblCovece.Location = new Point(lblCovece.Location.X, lblCovece.Location.Y -
25);
}
private void bDown_Click(object sender, EventArgs e)
{
lblCovece.Location = new Point(lblCovece.Location.X, lblCovece.Location.Y +
25);
}
private void bLeft_Click(object sender, EventArgs e)
{
lblCovece.Location = new Point(lblCovece.Location.X - 25,
lblCovece.Location.Y);
}
private void bRight_Click(object sender, EventArgs e)
{
lblCovece.Location = new Point(lblCovece.Location.X + 25,
lblCovece.Location.Y);
}
private void bCheck_Click(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
What makes your programm complicated and difficult to understand, is that you mix game logic with display logic.
I suggest you to redesign your game. It could look something like this:
public enum State
{
Empty, // Displays "Z"
Neutral, // Blue
Good, // Green
Bad // Red
}
public class Square
{
public int Number { get; set; }
public State State { get; set; }
}
public class Game
{
public const int Width = 7, Height = 6;
public Game()
{
Board = new Square[Width, Height];
}
public event EventHandler GameChanged;
public Square[,] Board { get; private set; }
public int CurrentX { get; private set; }
public int CurrentY { get; private set; }
public void Reset()
{
for (int x = 0; x < Width; x++) {
for (int y = 0; y < Height; y++) {
Board[x, y].State = State.Empty; // Always displayed in blue as "Z"
}
}
OnGameChanged();
}
public void MoveLeft()
{
if (CurrentX > 0) {
CurrentX--;
OnGameChanged();
}
}
public void MoveRight()
{
if (CurrentX < Width - 1) {
CurrentX++;
OnGameChanged();
}
}
// and so on
private void OnGameChanged()
{
EventHandler eh = GameChanged;
if (eh != null) {
eh(this, EventArgs.Empty);
}
}
}
In the form I would define pole to be a matrix as well (like the board). I show only a few relevant parts of the form code, to give you an idea of what I mean:
public class Form1 : Form
{
private Game game;
private Label[,] pole;
public Form1()
{
game = new Game();
game.GameChanged += new EventHandler(Game_GameChanged);
pole = new Label[Game.Width, Game.Height];
// Intialize pole.
// ...
}
void Game_GameChanged(object sender, EventArgs e)
{
for (int x = 0; x < Game.Width; x++) {
for (int y = 0; y < Game.Height; y++) {
Square square = game.Board[x, y];
Label label = pole[x,y];
switch (square.State) {
case State.Empty:
label.BackColor = Color.Blue;
label.Text = "Z";
break;
case State.Neutral:
label.BackColor = Color.Blue;
label.Text = square.Number.ToString();
break;
case State.Good:
label.BackColor = Color.Green;
label.Text = square.Number.ToString();
break;
case State.Bad:
label.BackColor = Color.Red;
label.Text = square.Number.ToString();
break;
default:
break;
}
}
}
// Place lblCovece according to game.CurrentX and game.CurrentY
// ...
}
private void bReset_Click(object sender, EventArgs e)
{
game.Reset();
}
private void bLeft_Click(object sender, EventArgs e)
{
game.MoveLeft();
}
private void bRight_Click(object sender, EventArgs e)
{
game.MoveRight();
}
}
Note how the Game class tells the form when changes happen through the event GameChanged. The form then updates the game display. In the game class, you can now concentrate on the game logic and do not have to deal with buttons and labels. You can also work with logical coordinates in the range [0..6] and [0..5] of the game board. This is easier than working with pixels. You delegate all the pixel calculations to the form.
My example is not complete, but when you try to implement it, you will see that it will be much easier to think about how the logic of the game should work.
Add an int score;
private void bCheck_Click(object sender, EventArgs e)
{
bool found = false;
foreach (Label l in pole)
{
if (l.Location == lblCovece.Location && l.Text == txtGoal.Text)
{
l.BackColor = Color.Green;
score += int.Parse(l.Text);
lblResultE.Text = score.ToString();
found = true;
}
}
if (!found)
{
score -= 10;
lblResultE.Text = score.ToString();
}
}