In the code below I am trying to zoom the image via a mouse wheel. But the code is not working properly, it is just refreshing the panel but it does not resize it. Actually I am taking the image from memory stream that is created by another class called as decrypt. Complete Image is displayed properly but I am not able to performing zooming of the image using mousewheel event.
Plz help Me.
private void Form2_Load(object sender, EventArgs e)
{
this.Width = Screen.PrimaryScreen.WorkingArea.Width;
this.Height = Screen.PrimaryScreen.WorkingArea.Height;
this.CenterToScreen();
PicturePanel= new PictureBox();
PicturePanel.Dock = DockStyle.Fill;
//PicturePanel.SizeMode = PictureBoxSizeMode.AutoSize;
//PicturePanel.SizeMode = PictureBoxSizeMode.CenterImage;
PicturePanel.Focus();
//PicturePanel.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.OnMouseWheel);
this.Controls.Add(PicturePanel);
View_codee v = new View_codee();
try
{
PicturePanel.Image = Image.FromStream(Decrypt.ms1);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
this.Name = "";
}
protected override void OnMouseWheel(MouseEventArgs mea)
{
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (PicturePanel.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (mea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((PicturePanel.Width < (15 * this.Width)) && (PicturePanel.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
PicturePanel.Width = (int)(PicturePanel.Width * 1.25);
PicturePanel.Height = (int)(PicturePanel.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
PicturePanel.Top = (int)(mea.Y - 1.25 * (mea.Y - PicturePanel.Top));
PicturePanel.Left = (int)(mea.X - 1.25 * (mea.X - PicturePanel.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((PicturePanel.Width > (this.Width / 15)) && (PicturePanel.Height > (this.Height / 15)))
{
// Change the size of the picturebox, divide it by the ZOOMFACTOR
PicturePanel.Width = (int)(PicturePanel.Width / 1.25);
PicturePanel.Height = (int)(PicturePanel.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
PicturePanel.Top = (int)(mea.Y - 0.80 * (mea.Y - PicturePanel.Top));
PicturePanel.Left = (int)(mea.X - 0.80 * (mea.X - PicturePanel.Left));
}
}
}
}
Source
Updated code by adding a new ImageProperty so you can set directly the Image;
public class PictureBox : System.Windows.Forms.UserControl
{
#region Members
private System.Windows.Forms.PictureBox PicBox;
private Panel OuterPanel;
private Container components = null;
private string m_sPicName = "";
#endregion
#region Constants
private double ZOOMFACTOR = 1.25; // = 25% smaller or larger
private int MINMAX = 5; // 5 times bigger or smaller than the ctrl
#endregion
#region Designer generated code
private void InitializeComponent()
{
this.PicBox = new System.Windows.Forms.PictureBox();
this.OuterPanel = new System.Windows.Forms.Panel();
this.OuterPanel.SuspendLayout();
this.SuspendLayout();
//
// PicBox
//
this.PicBox.Location = new System.Drawing.Point(0, 0);
this.PicBox.Name = "PicBox";
this.PicBox.Size = new System.Drawing.Size(150, 140);
this.PicBox.TabIndex = 3;
this.PicBox.TabStop = false;
//
// OuterPanel
//
this.OuterPanel.AutoScroll = true;
this.OuterPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.OuterPanel.Controls.Add(this.PicBox);
this.OuterPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.OuterPanel.Location = new System.Drawing.Point(0, 0);
this.OuterPanel.Name = "OuterPanel";
this.OuterPanel.Size = new System.Drawing.Size(210, 190);
this.OuterPanel.TabIndex = 4;
//
// PictureBox
//
this.Controls.Add(this.OuterPanel);
this.Name = "PictureBox";
this.Size = new System.Drawing.Size(210, 190);
this.OuterPanel.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
#region Constructors
public PictureBox()
{
InitializeComponent();
InitCtrl(); // my special settings for the ctrl
}
#endregion
#region Properties
private Image _pictureImage;
public Image PictureImage
{
get { return _pictureImage; }
set
{
if (null != value)
{
try
{
PicBox.Image = value;
_pictureImage = value;
}
catch (OutOfMemoryException ex)
{
RedCross();
}
}
else
{
RedCross();
}
}
}
/// <summary>
/// Property to select the picture which is displayed in the picturebox. If the
/// file doesn´t exist or we receive an exception, the picturebox displays
/// a red cross.
/// </summary>
/// <value>Complete filename of the picture, including path information</value>
/// <remarks>Supported fileformat: *.gif, *.tif, *.jpg, *.bmp</remarks>
///
[Browsable(false)]
public string Picture
{
get { return m_sPicName; }
set
{
if (null != value)
{
if (System.IO.File.Exists(value))
{
try
{
PicBox.Image = Image.FromFile(value);
m_sPicName = value;
}
catch (OutOfMemoryException ex)
{
RedCross();
}
}
else
{
RedCross();
}
}
}
}
/// <summary>
/// Set the frametype of the picturbox
/// </summary>
[Browsable(false)]
public BorderStyle Border
{
get { return OuterPanel.BorderStyle; }
set { OuterPanel.BorderStyle = value; }
}
#endregion
#region Other Methods
/// <summary>
/// Special settings for the picturebox ctrl
/// </summary>
private void InitCtrl()
{
PicBox.SizeMode = PictureBoxSizeMode.StretchImage;
PicBox.Location = new Point(0, 0);
OuterPanel.Dock = DockStyle.Fill;
OuterPanel.Cursor = System.Windows.Forms.Cursors.NoMove2D;
OuterPanel.AutoScroll = true;
OuterPanel.MouseEnter += new EventHandler(PicBox_MouseEnter);
PicBox.MouseEnter += new EventHandler(PicBox_MouseEnter);
OuterPanel.MouseWheel += new MouseEventHandler(PicBox_MouseWheel);
}
/// <summary>
/// Create a simple red cross as a bitmap and display it in the picturebox
/// </summary>
private void RedCross()
{
Bitmap bmp = new Bitmap(OuterPanel.Width, OuterPanel.Height, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
Graphics gr;
gr = Graphics.FromImage(bmp);
Pen pencil = new Pen(Color.Red, 5);
gr.DrawLine(pencil, 0, 0, OuterPanel.Width, OuterPanel.Height);
gr.DrawLine(pencil, 0, OuterPanel.Height, OuterPanel.Width, 0);
PicBox.Image = bmp;
gr.Dispose();
}
#endregion
#region Zooming Methods
/// <summary>
/// Make the PictureBox dimensions larger to effect the Zoom.
/// </summary>
/// <remarks>Maximum 5 times bigger</remarks>
private void ZoomIn()
{
if ((PicBox.Width < (MINMAX * OuterPanel.Width)) &&
(PicBox.Height < (MINMAX * OuterPanel.Height)))
{
PicBox.Width = Convert.ToInt32(PicBox.Width * ZOOMFACTOR);
PicBox.Height = Convert.ToInt32(PicBox.Height * ZOOMFACTOR);
PicBox.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
/// <summary>
/// Make the PictureBox dimensions smaller to effect the Zoom.
/// </summary>
/// <remarks>Minimum 5 times smaller</remarks>
private void ZoomOut()
{
if ((PicBox.Width > (OuterPanel.Width / MINMAX)) &&
(PicBox.Height > (OuterPanel.Height / MINMAX)))
{
PicBox.SizeMode = PictureBoxSizeMode.StretchImage;
PicBox.Width = Convert.ToInt32(PicBox.Width / ZOOMFACTOR);
PicBox.Height = Convert.ToInt32(PicBox.Height / ZOOMFACTOR);
}
}
#endregion
#region Mouse events
/// <summary>
/// We use the mousewheel to zoom the picture in or out
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PicBox_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta < 0)
{
ZoomIn();
}
else
{
ZoomOut();
}
}
/// <summary>
/// Make sure that the PicBox have the focus, otherwise it doesn´t receive
/// mousewheel events !.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PicBox_MouseEnter(object sender, EventArgs e)
{
if (PicBox.Focused == false)
{
PicBox.Focus();
}
}
#endregion
#region Disposing
/// <summary>
/// Die verwendeten Ressourcen bereinigen.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
}
private void Form2_Load(object sender, EventArgs e)
{
pictureBox1.PictureImage = Image.FromStream(Decrypt.ms1);
}
Related
The goal is to extract each time a frame from the video file then make histogram from the image and then to move to the next frame. this way all the frames.
The frames extraction and the histogram manipulation is working fine when the frames have saved as images on the hard disk. but now i want to do it all in memory.
to extract the frames i'm using ffmpeg because i think it's fast enough:
ffmpeg -r 1 -i MyVid.mp4 -r 1 "$filename%03d.png
for now i'm using the ffmpeg in command prompt window.
with this command it will save on the hard disk over 65000 images(frames).
but instead saving them on the hard disk i wonder if i can make the histogram manipulation on each frame in memory instead saving all the 65000 frames to the hard disk.
then i want to find specific images using the histogram and save to the hard disk this frames.
the histogram part for now is also using files from the hard disk and not from the memory:
private void btnLoadHistogram_Click(object sender, System.EventArgs e)
{
string[] files = Directory.GetFiles(#"d:\screenshots\", "*.jpg");
for (int i = 0; i < files.Length; i++)
{
sbInfo.Text = "Loading image";
if (pbImage.Image != null)
pbImage.Image.Dispose();
pbImage.Image = Image.FromFile(files[i]);//txtFileName.Text);
Application.DoEvents();
sbInfo.Text = "Computing histogram";
long[] myValues = GetHistogram(new Bitmap(pbImage.Image));
Histogram.DrawHistogram(myValues);
sbInfo.Text = "";
}
}
public long[] GetHistogram(System.Drawing.Bitmap picture)
{
long[] myHistogram = new long[256];
for (int i=0;i<picture.Size.Width;i++)
for (int j=0;j<picture.Size.Height;j++)
{
System.Drawing.Color c = picture.GetPixel(i,j);
long Temp=0;
Temp+=c.R;
Temp+=c.G;
Temp+=c.B;
Temp = (int) Temp/3;
myHistogram[Temp]++;
}
return myHistogram;
}
and the code of the class of the constrol HistogramaDesenat :
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace Histograma
{
/// <summary>
/// Summary description for HistogramaDesenat.
/// </summary>
public class HistogramaDesenat : System.Windows.Forms.UserControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public HistogramaDesenat()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// TODO: Add any initialization after the InitializeComponent call
this.Paint += new PaintEventHandler(HistogramaDesenat_Paint);
this.Resize+=new EventHandler(HistogramaDesenat_Resize);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// HistogramaDesenat
//
this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.Name = "HistogramaDesenat";
this.Size = new System.Drawing.Size(208, 176);
}
#endregion
private void HistogramaDesenat_Paint(object sender, PaintEventArgs e)
{
if (myIsDrawing)
{
Graphics g = e.Graphics;
Pen myPen = new Pen(new SolidBrush(myColor),myXUnit);
//The width of the pen is given by the XUnit for the control.
for (int i=0;i<myValues.Length;i++)
{
//We draw each line
g.DrawLine(myPen,
new PointF(myOffset + (i*myXUnit), this.Height - myOffset),
new PointF(myOffset + (i*myXUnit), this.Height - myOffset - myValues[i] * myYUnit));
//We plot the coresponding index for the maximum value.
if (myValues[i]==myMaxValue)
{
SizeF mySize = g.MeasureString(i.ToString(),myFont);
g.DrawString(i.ToString(),myFont,new SolidBrush(myColor),
new PointF(myOffset + (i*myXUnit) - (mySize.Width/2), this.Height - myFont.Height ),
System.Drawing.StringFormat.GenericDefault);
}
}
//We draw the indexes for 0 and for the length of the array beeing plotted
g.DrawString("0",myFont, new SolidBrush(myColor),new PointF(myOffset,this.Height - myFont.Height),System.Drawing.StringFormat.GenericDefault);
g.DrawString((myValues.Length-1).ToString(),myFont,
new SolidBrush(myColor),
new PointF(myOffset + (myValues.Length * myXUnit) - g.MeasureString((myValues.Length-1).ToString(),myFont).Width,
this.Height - myFont.Height),
System.Drawing.StringFormat.GenericDefault);
//We draw a rectangle surrounding the control.
g.DrawRectangle(new System.Drawing.Pen(new SolidBrush(Color.Black),1),0,0,this.Width-1,this.Height-1);
}
}
long myMaxValue;
private long[] myValues;
private bool myIsDrawing;
private float myYUnit; //this gives the vertical unit used to scale our values
private float myXUnit; //this gives the horizontal unit used to scale our values
private int myOffset = 20; //the offset, in pixels, from the control margins.
private Color myColor = Color.Black;
private Font myFont = new Font("Tahoma",10);
[Category("Histogram Options")]
[Description ("The distance from the margins for the histogram")]
public int Offset
{
set
{
if (value>0)
myOffset= value;
}
get
{
return myOffset;
}
}
[Category("Histogram Options")]
[Description ("The color used within the control")]
public Color DisplayColor
{
set
{
myColor = value;
}
get
{
return myColor;
}
}
/// <summary>
/// We draw the histogram on the control
/// </summary>
/// <param name="myValues">The values beeing draw</param>
public void DrawHistogram(long[] Values)
{
myValues = new long[Values.Length];
Values.CopyTo(myValues,0);
myIsDrawing = true;
myMaxValue = getMaxim(myValues);
ComputeXYUnitValues();
this.Refresh();
}
/// <summary>
/// We get the highest value from the array
/// </summary>
/// <param name="Vals">The array of values in which we look</param>
/// <returns>The maximum value</returns>
private long getMaxim(long[] Vals)
{
if (myIsDrawing)
{
long max = 0;
for (int i=0;i<Vals.Length;i++)
{
if (Vals[i] > max)
max = Vals[i];
}
return max;
}
return 1;
}
private void HistogramaDesenat_Resize(object sender, EventArgs e)
{
if (myIsDrawing)
{
ComputeXYUnitValues();
}
this.Refresh();
}
private void ComputeXYUnitValues()
{
myYUnit = (float) (this.Height - (2 * myOffset)) / myMaxValue;
myXUnit = (float) (this.Width - (2 * myOffset)) / (myValues.Length-1);
}
}
}
so in the end this is what i want to do :
extract the frames from the video file in memory using the ffmpeg.
instead using Directory.GetFiles i want to make the histogram manipulation on each frame from the memory that is extracted by the ffmpeg.
each extracted frame image to use the histogram to find if there is a lightning(weather lightning) in the image.
if there is a lightning save the frame image to the hard disk.
For ffmpeg, try FFmpeg.AutoGen
But you need learn about ffmpeg api for demuxer and decoder to get raw frame.
For opencv, try emgucv (recomend)
You can try search example somewhere like this
I'm creating a program that has a predefined amount of pictureboxes inserted by the user and then added in a List. the pictureboxs have to grow and each time they would hit eachother or hit the panel boundrys it would have been game over. I can't detect the collisions between them. Problem you can find in the section with 2 foreach. Thanks in advance
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 Boxen
{
public partial class FrmBoxes : Form
{
public FrmBoxes()
{
InitializeComponent();
}
//Defined Global Variables for the User input, points, the biggest box, total of points and the average of points
int UserInput;
int Points = 0, BiggestBox = 0, PointsTotal =0;
double AveragePoints = 0;
//List of points of each box and another one to insert the boxes
List<int> PointsList = new List<int>();
List<PictureBox> picboxsList = new List<PictureBox>();
//Random generator for color in the boxes
Random rnd = new Random();
/// <summary>
/// FrmBoxes_Load means when the formular will load, than Info will be shown and Timer will be set to false
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FrmBoxes_Load(object sender, EventArgs e)
{
// Info will be displayed
LblInfo.Text = "Click Box, as bigger as more points you get";
//Timer set to false
TmrFirst.Enabled = false;
}
/// <summary>
/// TmrFirst_Tick, each time the Timer will tick will write on 3 labels different values according with the total points, average points and size of box es
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TmrFirst_Tick(object sender, EventArgs e)
{
//this tree labels will be written from their variables values and the value that its in that variable will be connverted to string
LblPoints.Text = Convert.ToString(PointsTotal);
LblClickPoints.Text = Convert.ToString(AveragePoints);
LblBiggestBox.Text = Convert.ToString(BiggestBox);
//Loop that counts betwwen 0 and the amount of Boxes inside of the List
for (int i = 0; i < picboxsList.Count; i++)
{
//The index of the List will gain a new size of 2 pixels in height and width at each tick
picboxsList[i].Size = new Size(picboxsList[i].Width + 2, picboxsList[i].Height + 2);
}
//Verificacion if the amount of boxes in the List is inferior to the number inputed bby the user
if (picboxsList.Count < UserInput)
{
//Object Box is created with 20 Pixels height and widht and a random color is generated for their background
PictureBox picBox = new PictureBox();
picBox.Size = new Size(20, 20);
picBox.BackColor = Color.FromArgb(rnd.Next(10, 245), rnd.Next(10, 245), rnd.Next(10, 245));
//Event handler will be added to click the box event
picBox.Click += new EventHandler(PicBox_Click);
//Box will be added to the game field with 30 Pixels distance to the edges
PnlGameField.Controls.Add(picBox);
picBox.Location = new Point(rnd.Next(0, PnlGameField.Width - 30), rnd.Next(0, PnlGameField.Height - 30));
//Each created box will be added to the List
picboxsList.Add(picBox);
foreach (Control x in this.Controls)
{
foreach (Control y in this.Controls)
{
if (x != y)
{
if (x.Bounds.IntersectsWith(y.Bounds)) //&& (picBox.Bounds.IntersectsWith(x.Bounds)PnlGameField.Height)) && (picBox.Bounds.IntersectsWith(x.Bounds)PnlGameField.Width))
{
LblInfo.Text = "Game Over";
TmrFirst.Stop();
}
}
}
}
}
//Check that the list is not nothing and contains more than 1 element before continuing.
//for (outerIndex as Int32 = 0; picboxsList.Count - 2)
//for (innerIndex as Int32 = outerIndex + 1; picboxsList.Count - 1)
//if (picboxsList(outerIndex).Bounds.IntersectsWith(picboxsList(innerIndex).Bounds))
//Then slimeList(outerIndex) and slimeList(innerIndex) have collided.
}
/// <summary>
/// Pic_Box, event handler created at click, will add some values and remove after click
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PicBox_Click(object sender, EventArgs e)
{
//event handler called with click interaccion
PictureBox picBox = (PictureBox)sender;
//remove the box from the game field
picBox.Dispose();
//remove the box from the List
picboxsList.Remove(picBox);
//Width will be used to calculate the total points and add each time that is clicked//
PointsTotal += picBox.Width;
//Width will be used to calculate the points per click
Points = picBox.Width;
//Points will be added to the List
PointsList.Add(Points);
//Max value from a box will be token out from the list Points and saved in the variable BiggestBox
BiggestBox = PointsList.Max();
//AveragePoints variable will hold the double value of all the boxes points saved in the list and will update upon click in each box
AveragePoints = Convert.ToDouble(PointsList.Average());
}
/// <summary>
/// When start button will be clicked it creates the amount of boxes defined by the user
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnStart_Click(object sender, EventArgs e)
{
//new text inserted in the label LblInfo to be displayed upon click on the button start
LblInfo.Text = "When the Boxes hit eachother or the edges it's Game Over";
//User input converted to integer and displayed in text box
UserInput = Convert.ToInt32(TxtNumberOfBoxes.Text);
//Timer will be set to true
TmrFirst.Enabled = true;
//Loop to check the user input and add boxes till his given number
for (int i = 0; i < UserInput; i++)
{
//Object Box is created with 20 Pixels height and widht and a random color is generated for their background
PictureBox picBox = new PictureBox();
picBox.Size = new Size(20, 20);
picBox.BackColor = Color.FromArgb(rnd.Next(10, 245), rnd.Next(10, 245), rnd.Next(10, 245));
//Event handler will be added to click the box event
picBox.Click += new EventHandler(PicBox_Click);
//Box will be added to the game field with 30 Pixels distance to the edges
PnlGameField.Controls.Add(picBox);
picBox.Location = new Point(rnd.Next(0, PnlGameField.Width - 30), rnd.Next(0, PnlGameField.Height - 30));
//Each created box will be added to the List
picboxsList.Add(picBox);
}
}
}
}
Answer
Your collision detection does not work because you are checking the controls of the form, not of the PnlGameField. Use the following game loop instead:
private void timer1_Tick(object sender, EventArgs e)
{
var boxes = PnlGameField.Controls.OfType<PictureBox>().ToArray();
foreach (var box in boxes)
{
box.Size = new Size(box.Size.Width + 2, box.Size.Height + 2);
box.Location = new Point(box.Location.X - 1, box.Location.Y - 1);
}
if (CheckCollisions(boxes))
{
EndGame();
}
PnlGameField.Invalidate();
}
private bool CheckCollisions(PictureBox[] boxes)
{
for (int i = 0; i < boxes.Length; i++)
{
var box = boxes[i];
if (box.Left < 0 || box.Right >= PnlGameField.Width
|| box.Top < 0 || box.Bottom >= PnlGameField.Height)
{
box.BorderStyle = BorderStyle.FixedSingle;
return true;
}
for (int j = i+1; j < boxes.Length; j++)
{
var other = boxes[j];
if (box.Bounds.IntersectsWith(other.Bounds))
{
box.BorderStyle = BorderStyle.FixedSingle;
other.BorderStyle = BorderStyle.FixedSingle;
return true;
}
}
}
return false;
}
which I tested and it works 🗸.
Other interesting points.
Scoring
Create a class to keep track of scoring statistics. Convert the points into float since double is too precise (you get averages of 29.9999999997 instead of 30.0) and your conversion to double after averaging is a bug.
public class GameScore
{
readonly List<float> pointsList;
public GameScore()
{
pointsList = new List<float>();
}
public int BoxCount { get => pointsList.Count; }
public float Average { get => pointsList.Count >0 ? pointsList.Average() : 0; }
public float Maximum { get => pointsList.Count>0 ? pointsList.Max() : 0; }
public float Total { get => pointsList.Count>0 ? pointsList.Sum() : 0; }
public void Add(int points)
{
pointsList.Add(points);
}
public void Reset()
{
pointsList.Clear();
}
public override string ToString()
{
return $"Total={Total}, Ave={Average}, Max={Maximum}";
}
}
And the GameScore can be used each time a box is clicked with score.Add(box.Width). See source code below for more details
Box Creation/Removal
One other point to make is you only need to add a new box after one is clicked and removed, so there is no need to check if the current count == user input, and there is no need to duplicate code for adding boxes. Put in a function and call it as needed.
This a general observation that you will be better served if you split your code into functional units (add functions) and call them from the UI handlers. There is no need to keep a separate list of picture boxes, and you can know if the game is running or not by using the Timer.Enabled property.
Source Listing
Below is the full code listing I used for testing, and it largely based on your code, but with things re-arranged around. My hope is that you can get inspired to understand how to be better at structuring your code.
public partial class Form1 : Form
{
static Random rnd = new Random();
readonly GameScore score = new GameScore();
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
timer1.Enabled = false;
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
if (timer1.Enabled)
{
EndGame();
}
else
{
StartGame();
}
}
private void StartGame()
{
toolStripButton1.Text = "Stop";
toolStripButton1.BackColor = Color.FromArgb(255, 128, 128);
if (int.TryParse(toolStripTextBox1.Text, out int count) && count > 0)
{
PnlGameField.Controls.Clear();
for (int i = 0; i < count; i++)
{
AddRandomBox();
}
score.Reset();
toolStripTextBox2.Text = score.ToString();
timer1.Enabled = true;
}
}
private void EndGame()
{
toolStripButton1.Text = "Start";
toolStripButton1.BackColor = Color.FromArgb(128, 255, 128);
timer1.Enabled = false;
}
private void AddRandomBox()
{
//Object Box is created with 20 Pixels height and width and a random color is generated for their background
PictureBox picBox = new PictureBox();
picBox.Size = new Size(20, 20);
picBox.BackColor = Color.FromArgb(rnd.Next(10, 245), rnd.Next(10, 245), rnd.Next(10, 245));
//Event handler will be added to click the box event
picBox.Click += PicBox_Click;
//Box will be added to the game field with 30 Pixels distance to the edges
PnlGameField.Controls.Add(picBox);
picBox.Location = new Point(rnd.Next(0, PnlGameField.Width - 30), rnd.Next(0, PnlGameField.Height - 30));
}
private void PicBox_Click(object sender, EventArgs e)
{
var target = (PictureBox)sender;
if (timer1.Enabled)
{
RemoveBox(target);
}
}
private void RemoveBox(PictureBox box)
{
score.Add(box.Width);
box.Dispose();
PnlGameField.Controls.Remove(box);
AddRandomBox();
toolStripTextBox2.Text = score.ToString();
PnlGameField.Invalidate();
}
private void timer1_Tick(object sender, EventArgs e)
{
var boxes = PnlGameField.Controls.OfType<PictureBox>().ToArray();
foreach (var box in boxes)
{
box.Size = new Size(box.Size.Width + 2, box.Size.Height + 2);
box.Location = new Point(box.Location.X - 1, box.Location.Y - 1);
}
if (CheckCollisions(boxes))
{
EndGame();
}
PnlGameField.Invalidate();
}
private bool CheckCollisions(PictureBox[] boxes)
{
for (int i = 0; i < boxes.Length; i++)
{
var box = boxes[i];
if (box.Left < 0 || box.Right >= PnlGameField.Width
|| box.Top < 0 || box.Bottom >= PnlGameField.Height)
{
box.BorderStyle = BorderStyle.FixedSingle;
return true;
}
for (int j = i+1; j < boxes.Length; j++)
{
var other = boxes[j];
if (box.Bounds.IntersectsWith(other.Bounds))
{
box.BorderStyle = BorderStyle.FixedSingle;
other.BorderStyle = BorderStyle.FixedSingle;
return true;
}
}
}
return false;
}
}
I want to make a window to slide up from the bottom of the screen when it opens. However im having an issue with my code, im getting the following error when adding my Method to the Loaded event
Error:
Additional information: Value cannot be null.
This is the code which adds the Method to the Eventhandler, and the Method:
//Position the Notification
var workingArea = SystemParameters.WorkArea;
this.Left = (workingArea.Width - this.ActualWidth) / 2;
//Create storyboard for animation
Loaded += animate;
}
}
}
public RoutedEventHandler animate(object sender, RoutedEventArgs e)
{
var workingArea = SystemParameters.WorkArea;
Storyboard sb = new Storyboard();
var slide = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(2),
Duration = TimeSpan.FromSeconds(1),
By = -100
};
Storyboard.SetTarget(slide,this);
Storyboard.SetTargetName(this, "MyWindow");
Storyboard.SetTargetProperty(slide,new PropertyPath("(Window.Top)"));
this.Top = workingArea.Height - this.ActualHeight;
return null;
}
Edit:
This is the entire Window Code Behind, which should handle the animations and positioning.
/// <summary>
/// Interaction logic for NotificationAll.xaml
/// </summary>
public partial class NotificationAll : Window
{
public NotificationAll() : base()
{
InitializeComponent();
}
public new void Show()
{
//Ensure new notifications are placed above older ones
if (!Application.Current.Windows.Cast<Window>().Where(x =>
x != this).Any(x => x.GetType().Name == "NotificationAll"))
{
this.Topmost = true;
base.Show();
this.Owner = System.Windows.Application.Current.MainWindow;
//Position the Notification
var workingArea = SystemParameters.WorkArea;
this.Left = (workingArea.Width - this.ActualWidth) / 2;
//Create storyboard for animation
Loaded += SlideFromBottom;
}
}
public void SlideFromBottom(object sender, RoutedEventArgs e)
{
MessageBox.Show("h");
var workingArea = SystemParameters.WorkArea;
Storyboard sb = new Storyboard();
var slide = new DoubleAnimation()
{
BeginTime = TimeSpan.FromSeconds(2),
Duration = TimeSpan.FromSeconds(1),
By = -100
};
Storyboard.SetTarget(slide,this);
Storyboard.SetTargetName(this, "MyWindow");
Storyboard.SetTargetProperty(slide,new PropertyPath("(Window.Top)"));
this.Top = workingArea.Height - this.ActualHeight;
}
/// <summary>
/// Close window once animation is complete
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DoubleAnimationCompleted(object sender, EventArgs e)
{
if (!this.IsMouseOver)
{
this.Close();
}
}
}
Well, it's pretty simple. You are calling your animate method and assigning the result of it to the Loaded event. The animate method always returns null in your case. If you want animate to be an event handler, you should not call it using the parenthesis. You should instead do Loaded += animate;
And it should have the correct signature: void animate(object sender, RoutedEventArgs e)
I have a control (System.Windows.Forms.ScrollableControl) which can potentially be very large. It has custom OnPaint logic. For that reason, I am using the workaround described here.
public class CustomControl : ScrollableControl
{
public CustomControl()
{
this.AutoScrollMinSize = new Size(100000, 500);
this.DoubleBuffered = true;
}
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
graphics.Clear(this.BackColor);
...
}
}
The painting code mainly draws "normal" things that move when you scroll. The origin of each shape that is drawn is offsetted by this.AutoScrollPosition.
graphics.DrawRectangle(pen, 100 + this.AutoScrollPosition.X, ...);
However, the control also contains "static" elements, which are always drawn at the same position relative to the parent control. For that, I just don't use AutoScrollPosition and draw the shapes directly:
graphics.DrawRectangle(pen, 100, ...);
When the user scrolls, Windows translates the entire visible area in the direction opposite to the scrolling. Usually this makes sense, because then the scrolling seems smooth and responsive (and only the new part has to be redrawn), however the static parts are also affected by this translation (hence the this.Invalidate() in OnScroll). Until the next OnPaint call has successfully redrawn the surface, the static parts are slightly off. This causes a very noticable "shaking" effect when scrolling.
Is there a way I can create a scrollable custom control that does not have this problem with static parts?
You could do this by taking full control of scrolling. At the moment, you're just hooking in to the event to do your logic. I've faced issues with scrolling before, and the only way I've ever managed to get everything to work smoothly is by actually handling the Windows messages by overriding WndProc. For instance, I have this code to synchronize scrolling between several ListBoxes:
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
// 0x115 and 0x20a both tell the control to scroll. If either one comes
// through, you can handle the scrolling before any repaints take place
if (m.Msg == 0x115 || m.Msg == 0x20a)
{
//Do you scroll processing
}
}
Using WndProc will get you the scroll messages before anything gets repainted at all, so you can appropriately handle the static objects. I'd use this to suspend scrolling until an OnPaint occurs. It won't look as smooth, but you won't have issues with the static objects moving.
Since I really needed this, I ended up writing a Control specifically for the case when you have static graphics on a scrollable surface (whose size can be greater than 65535).
It is a regular Control with two ScrollBar controls on it, and a user-assignable Control as its Content. When the user scrolls, the container sets its Content's AutoScrollOffset accordingly. Therefore, it is possible to use controls which use the AutoScrollOffset method for drawing without changing anything. The Content's actual size is exactly the visible part of it at all times. It allows horizontal scrolling by holding down the shift key.
Usage:
var container = new ManuallyScrollableContainer();
var content = new ExampleContent();
container.Content = content;
container.TotalContentWidth = 150000;
container.TotalContentHeight = 5000;
container.Dock = DockStyle.Fill;
this.Controls.Add(container); // e.g. add to Form
Code:
It became a bit lengthy, but I could avoid ugly hacks. Should work with mono. I think it turned out pretty sane.
public class ManuallyScrollableContainer : Control
{
public ManuallyScrollableContainer()
{
InitializeControls();
}
private class UpdatingHScrollBar : HScrollBar
{
protected override void OnValueChanged(EventArgs e)
{
base.OnValueChanged(e);
// setting the scroll position programmatically shall raise Scroll
this.OnScroll(new ScrollEventArgs(ScrollEventType.EndScroll, this.Value));
}
}
private class UpdatingVScrollBar : VScrollBar
{
protected override void OnValueChanged(EventArgs e)
{
base.OnValueChanged(e);
// setting the scroll position programmatically shall raise Scroll
this.OnScroll(new ScrollEventArgs(ScrollEventType.EndScroll, this.Value));
}
}
private ScrollBar shScrollBar;
private ScrollBar svScrollBar;
public ScrollBar HScrollBar
{
get { return this.shScrollBar; }
}
public ScrollBar VScrollBar
{
get { return this.svScrollBar; }
}
private void InitializeControls()
{
this.Width = 300;
this.Height = 300;
this.shScrollBar = new UpdatingHScrollBar();
this.shScrollBar.Top = this.Height - this.shScrollBar.Height;
this.shScrollBar.Left = 0;
this.shScrollBar.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
this.svScrollBar = new UpdatingVScrollBar();
this.svScrollBar.Top = 0;
this.svScrollBar.Left = this.Width - this.svScrollBar.Width;
this.svScrollBar.Anchor = AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
this.shScrollBar.Width = this.Width - this.svScrollBar.Width;
this.svScrollBar.Height = this.Height - this.shScrollBar.Height;
this.Controls.Add(this.shScrollBar);
this.Controls.Add(this.svScrollBar);
this.shScrollBar.Scroll += this.HandleScrollBarScroll;
this.svScrollBar.Scroll += this.HandleScrollBarScroll;
}
private Control _content;
/// <summary>
/// Specifies the control that should be displayed in this container.
/// </summary>
public Control Content
{
get { return this._content; }
set
{
if (_content != value)
{
RemoveContent();
this._content = value;
AddContent();
}
}
}
private void AddContent()
{
if (this.Content != null)
{
this.Content.Left = 0;
this.Content.Top = 0;
this.Content.Width = this.Width - this.svScrollBar.Width;
this.Content.Height = this.Height - this.shScrollBar.Height;
this.Content.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
this.Controls.Add(this.Content);
CalculateMinMax();
}
}
private void RemoveContent()
{
if (this.Content != null)
{
this.Controls.Remove(this.Content);
}
}
protected override void OnParentChanged(EventArgs e)
{
// mouse wheel events only arrive at the parent control
if (this.Parent != null)
{
this.Parent.MouseWheel -= this.HandleMouseWheel;
}
base.OnParentChanged(e);
if (this.Parent != null)
{
this.Parent.MouseWheel += this.HandleMouseWheel;
}
}
private void HandleMouseWheel(object sender, MouseEventArgs e)
{
this.HandleMouseWheel(e);
}
/// <summary>
/// Specifies how the control reacts to mouse wheel events.
/// Can be overridden to adjust the scroll speed with the mouse wheel.
/// </summary>
protected virtual void HandleMouseWheel(MouseEventArgs e)
{
// The scroll difference is calculated so that with the default system setting
// of 3 lines per scroll incremenet,
// one scroll will offset the scroll bar value by LargeChange / 4
// i.e. a quarter of the thumb size
ScrollBar scrollBar;
if ((Control.ModifierKeys & Keys.Shift) != 0)
{
scrollBar = this.HScrollBar;
}
else
{
scrollBar = this.VScrollBar;
}
var minimum = 0;
var maximum = scrollBar.Maximum - scrollBar.LargeChange;
if (maximum <= 0)
{
// happens when the entire area is visible
return;
}
var value = scrollBar.Value - (int)(e.Delta * scrollBar.LargeChange / (120.0 * 12.0 / SystemInformation.MouseWheelScrollLines));
scrollBar.Value = Math.Min(Math.Max(value, minimum), maximum);
}
public event ScrollEventHandler Scroll;
protected virtual void OnScroll(ScrollEventArgs e)
{
var handler = this.Scroll;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Event handler for the Scroll event of either scroll bar.
/// </summary>
private void HandleScrollBarScroll(object sender, ScrollEventArgs e)
{
OnScroll(e);
if (this.Content != null)
{
this.Content.AutoScrollOffset = new System.Drawing.Point(-this.HScrollBar.Value, -this.VScrollBar.Value);
this.Content.Invalidate();
}
}
private int _totalContentWidth;
public int TotalContentWidth
{
get { return _totalContentWidth; }
set
{
if (_totalContentWidth != value)
{
_totalContentWidth = value;
CalculateMinMax();
}
}
}
private int _totalContentHeight;
public int TotalContentHeight
{
get { return _totalContentHeight; }
set
{
if (_totalContentHeight != value)
{
_totalContentHeight = value;
CalculateMinMax();
}
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
CalculateMinMax();
}
private void CalculateMinMax()
{
if (this.Content != null)
{
// Reduced formula according to
// http://msdn.microsoft.com/en-us/library/system.windows.forms.scrollbar.maximum.aspx
// Note: The original formula is bogus.
// According to the article, LargeChange has to be known in order to calculate Maximum,
// however, that is not always possible because LargeChange cannot exceed Maximum.
// If (LargeChange) == (1 * visible part of control), the formula can be reduced to:
if (this.TotalContentWidth > this.Content.Width)
{
this.shScrollBar.Enabled = true;
this.shScrollBar.Maximum = this.TotalContentWidth;
}
else
{
this.shScrollBar.Enabled = false;
}
if (this.TotalContentHeight > this.Content.Height)
{
this.svScrollBar.Enabled = true;
this.svScrollBar.Maximum = this.TotalContentHeight;
}
else
{
this.svScrollBar.Enabled = false;
}
// this must be set after the maximum is determined
this.shScrollBar.LargeChange = this.shScrollBar.Width;
this.shScrollBar.SmallChange = this.shScrollBar.LargeChange / 10;
this.svScrollBar.LargeChange = this.svScrollBar.Height;
this.svScrollBar.SmallChange = this.svScrollBar.LargeChange / 10;
}
}
}
Example content:
public class ExampleContent : Control
{
public ExampleContent()
{
this.DoubleBuffered = true;
}
static Random random = new Random();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
// random color to make the clip rectangle visible in an unobtrusive way
var color = Color.FromArgb(random.Next(160, 180), random.Next(160, 180), random.Next(160, 180));
graphics.Clear(color);
Debug.WriteLine(this.AutoScrollOffset.X.ToString() + ", " + this.AutoScrollOffset.Y.ToString());
CheckerboardRenderer.DrawCheckerboard(
graphics,
this.AutoScrollOffset,
e.ClipRectangle,
new Size(50, 50)
);
StaticBoxRenderer.DrawBoxes(graphics, new Point(0, this.AutoScrollOffset.Y), 100, 30);
}
}
public static class CheckerboardRenderer
{
public static void DrawCheckerboard(Graphics g, Point origin, Rectangle bounds, Size squareSize)
{
var numSquaresH = (bounds.Width + squareSize.Width - 1) / squareSize.Width + 1;
var numSquaresV = (bounds.Height + squareSize.Height - 1) / squareSize.Height + 1;
var startBoxH = (bounds.X - origin.X) / squareSize.Width;
var startBoxV = (bounds.Y - origin.Y) / squareSize.Height;
for (int i = startBoxH; i < startBoxH + numSquaresH; i++)
{
for (int j = startBoxV; j < startBoxV + numSquaresV; j++)
{
if ((i + j) % 2 == 0)
{
Random random = new Random(i * j);
var color = Color.FromArgb(random.Next(70, 95), random.Next(70, 95), random.Next(70, 95));
var brush = new SolidBrush(color);
g.FillRectangle(brush, i * squareSize.Width + origin.X, j * squareSize.Height + origin.Y, squareSize.Width, squareSize.Height);
brush.Dispose();
}
}
}
}
}
public static class StaticBoxRenderer
{
public static void DrawBoxes(Graphics g, Point origin, int boxWidth, int boxHeight)
{
int height = origin.Y;
int left = origin.X;
for (int i = 0; i < 25; i++)
{
Rectangle r = new Rectangle(left, height, boxWidth, boxHeight);
g.FillRectangle(Brushes.White, r);
g.DrawRectangle(Pens.Black, r);
height += boxHeight;
}
}
}
I am using a code for wia scanner. but after 75dpi the scanner does not give me full Image. I am getting only the half image from the scanner .please check this out.
private static void SaveImageToPNGFile(ImageFile image, string fileName)
{
ImageProcess imgProcess = new ImageProcess();
object convertFilter = "Convert";
string convertFilterID = imgProcess.FilterInfos.get_Item(ref convertFilter).FilterID;
imgProcess.Filters.Add(convertFilterID, 0);
SetWIAProperty(imgProcess.Filters[imgProcess.Filters.Count].Properties, "FormatID", WIA.FormatID.wiaFormatPNG);
image = imgProcess.Apply(image);
image.SaveFile(fileName);
}
private static void AdjustScannerSettings(IItem scannnerItem, int scanColor,
int scanWidthPixels, int scanHeightPixels, int brightnessPercents, int contrastPercents)
{
SetWIAProperty(scannnerItem.Properties, "4104", 24);
SetWIAProperty(scannnerItem.Properties, "6146", scanColor);
SetWIAProperty(scannnerItem.Properties, "6147", 75);//Horizontal resolution
SetWIAProperty(scannnerItem.Properties, "6148", 75);//vertical resolution
SetWIAProperty(scannnerItem.Properties, "6151", scanHeightPixels);
SetWIAProperty(scannnerItem.Properties, "6152", scanWidthPixels);
SetWIAProperty(scannnerItem.Properties, "6154", brightnessPercents);//range from -100 to 100. 0 refer for the normal brightnes
SetWIAProperty(scannnerItem.Properties, "6155", contrastPercents);//range from -100 to 100. 0 refer for the normal brightnes
}
private static void SetWIAProperty(IProperties properties, object propName, object propValue)
{
Property prop = properties.get_Item(ref propName);
prop.set_Value(ref propValue);
}
private void button1_Click(object sender, EventArgs e)
{
int height = int.Parse(textBoxheight.Text.Trim());
int width = int.Parse(textBoxwidth.Text.Trim());
CommonDialogClass commonDialogClass = new CommonDialogClass();
Device scannerDevice;
try
{
scannerDevice = commonDialogClass.ShowSelectDevice(WiaDeviceType.ScannerDeviceType, false , false);
if (scannerDevice != null)
{
Item scannnerItem = scannerDevice.Items[1];
AdjustScannerSettings(scannnerItem, 4, height, width, 20, 20);//dpi valid only for 150,200,300,400
object scanResult = commonDialogClass.ShowTransfer(scannnerItem, WIA.FormatID.wiaFormatPNG, false);
if (scanResult != null)
{
ImageFile image = (ImageFile)scanResult;
// Image objimg = (Image)image;
string fileName = Path.GetTempPath() + DateTime.Now.ToString("dd-MM-yyyy-hh-mm-ss-fffffff") + ".png";
SaveImageToPNGFile(image, fileName);
pictureBox1.ImageLocation = fileName;
//pictureBox1.Height = height;
//pictureBox1.Width = width;
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
Bitmap obj = new Bitmap(fileName);
panel1.BackgroundImage = obj;
panel1.AutoSize = true;
panel1.AutoScroll = true;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void button3_Click(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
private void button2_Click(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Hide();
wiascan obj = new wiascan();
obj.Show();
}
}
I tried in both in a pic box and a panel to show my Image but both control could not give me the full image.
using System;
using System.Windows.Forms;
using WIA;
class Program
{
static void Main(string[] args)
{
try
{
// Find a device.
var deviceManager = new DeviceManager();
var device = deviceManager.DeviceInfos[1].Connect();
var item = device.Items[1];
// Check if device exist.
if(item == null)
{
MessageBox.Show("Scanner not found!", "Status");
return;
}
// Set the properties of the device.
setItem(item, "6146", 1); // <- 4 black and white, 2 grayscale, 1 color, 0 unspecified.
setItem(item, "6147", 200); // <- Horizontal resolution.
setItem(item, "6148", 200); // <- Veritcal resolution.
setItem(item, "6151", 200 * 8.5); // <- Horizontal scanning area.
setItem(item, "6152", 200 * 11); // <- Vertical scanning area.
setItem(item, "6149", 0); // <- Horizontal pixel start.
setItem(item, "6150", 0); // <- Vertical pixel start.
var properties = item.Properties;
// Scan.
var imageFile = (ImageFile)item.Transfer("{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}");
// Use to compress jpeg.
WIA.ImageProcess myip = new WIA.ImageProcess();
myip.Filters.Add(myip.FilterInfos["Convert"].FilterID);
myip.Filters[1].Properties["FormatID"].set_Value("{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}");
myip.Filters[1].Properties["Quality"].set_Value(99);
// Create image an apply our image with the filter.
ImageFile image = myip.Apply(imageFile);
// Save to a given path.
var path = "image.jpg";
image.SaveFile(path);
MessageBox.Show("Scan complete", "Status");
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Status");
}
}
/// <summary>
/// Set scanner properties.
/// </summary>
/// <param name="item"></param>
/// <param name="property"></param>
/// <param name="value"></param>
static void setItem(IItem item, object property, object value)
{
Property aProperty = item.Properties.get_Item(ref property);
aProperty.set_Value(ref value);
}
}