I have the code below which is an implementation of a marquee label. I'd like the marquee label to run at the most efficient rate possible on the computer while maintaining a constant speed across different computers. I'd like the amount of "frames" that are skipped to be minimized so that the program takes full advantage of the computer it's being run on. But at the same time I don't necessarily want to consume 100% of the CPU (are those statements contradictory? I'm not sure).
Currently I'm sleeping 10 milliseconds for each iteration of the animation loop. This feels wasteful to me and it seems like it might slow down the animation on slower computers that might need those extra 10 milliseconds. I'm not sure what the best value is to use in the sleep method or even if I should sleep at all. I've read some about Sleep(0) and Sleep(1) and Yield and SpinWait, but I can't make sense of it all.
Also is it a bad thing to call Invalidate too much? Can I overload it by calling it too much?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Diagnostics;
using Timer = System.Timers.Timer;
using System.Threading;
namespace Scoreboard
{
public class MarqueeLabel : Label
{
private float _marqueeOffset;
private float _marqueeMeasurement;
public MarqueeLabel()
{
UseCompatibleTextRendering = true;
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
Thread t = new Thread(ThreadRun);
t.IsBackground = true;
t.Start();
}
public void ThreadRun()
{
long time = DateTime.Now.Ticks;
long diff = 0;
while (true)
{
float step = -((float)diff / (20 * TimeSpan.TicksPerMillisecond)); //change "frame" based on the elapsed time
_marqueeOffset = _marqueeOffset >= -_marqueeMeasurement ? _marqueeOffset + step : Width;
Invalidate();
Thread.Sleep(10); // how long should i wait here?
long temp = DateTime.Now.Ticks;
diff = temp - time;
time = temp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
stringFormat.Trimming = StringTrimming.None;
stringFormat.Alignment = StringAlignment.Near;
Rectangle rect = ClientRectangle;
rect.X += (int)_marqueeOffset;
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), rect, stringFormat);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
MeasureText();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
MeasureText();
}
void MeasureText()
{
_marqueeMeasurement = CreateGraphics().MeasureString(Text, Font).Width;
}
}
}
Related
I'm working on a program in which I want to move a character composed by several shapes (rectangle,circle,point) but when the character moves, it flickers.
I have tried several solutions like change the DoubleBuffered, SetStyle but they doesn't work and I don't know how i can do this.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Carrosse
{
public partial class EcranAccueil : Form
{
private Capitaine cptHaddock;
private BufferedGraphics bufferG = null;
private Graphics g;
public EcranAccueil()
{
InitializeComponent();
// Modification contre le scintillement - Creation d'une mémoire tampon graphique
bufferG = BufferedGraphicsManager.Current.Allocate(TV.CreateGraphics(), TV.DisplayRectangle);
g = bufferG.Graphics;
}
private void btnCreercptHaddock_Click(object sender, EventArgs e)
{
this.cptHaddock = new Capitaine(this.TV, 50, 80, 60, 20);
this.cptHaddock.Pot = Color.Blue;
this.cptHaddock.Afficher(this.TV.Handle);
this.btnMarcherSimple.Enabled = true;
this.btnChangerRapidite.Enabled = true;
this.btnMarcherContinu.Enabled = true;
}
private void btnMarcherSimple_Click(object sender, EventArgs e)
{
this.cptHaddock.AvancerDroit(this.TV.Handle, this.timerImage, 16, 0);
this.cptHaddock.AvancerGauche(this.TV.Handle, this.timerImage, 16, 0);
}
private void btnChangerRapidite_Click(object sender, EventArgs e)
{
try
{
this.timerImage.Interval = Int32.Parse(tbRapidite.Text);
}
catch (Exception)
{
tbRapidite.Text = "Valeur non valide !";
}
}
private void btnMarcherContinu_Click(object sender, EventArgs e)
{
for (int i = 0; i < 4; i++)
{
this.cptHaddock.AvancerDroit(this.TV.Handle, this.timerImage, 16, 0);
this.cptHaddock.AvancerGauche(this.TV.Handle, this.timerImage, 16, 0);
}
}
}
}
The button btnCreercptHaddock creates the character.
The buttons btnMarcherSimple and btnMarcherContinu moves the character.
The button changes the speed of the character via a timer with the help of an input.
I can provide the others parts of my program but I have something like 10 classes.
PS : I'm student and I'm learning C# so my code is maybe not optimized so if you have comments or advices, I would be happy to hear them.
EDIT: gif of the animation : https://i.gyazo.com/68ded3fba8fddbf281a424bbb523f2d2.gif (All the flickers don't appear in the gif but they appear during all the animation)
Set this code in your form. It will remove the flicker. By setting ExStyle which is actually WS_EX_TRANSPARENT flag, it makes window control invisible to mouse events, which in turn reduces significant amount of flicker.
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; //WS_EX_TRANSPARENT flag
return cp;
}
}
Hope it helps
I'm having a problem with displaying transparent images with a transparent background. The transparent background takes the color of the underlying control and that is fine ... bu the problem is that some details (lines) on the underlying background are being covered the the images as can be seen in the image below.
Here is the code I am using.... This is the code for the notes....
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Media;
using System.Drawing;
namespace Simpe_Piano_new
{
class MusicNote: PictureBox
{
public SoundPlayer sp = new SoundPlayer();
Timer tmr = new Timer();
public int pitch; //The no. of the music key (e.g. the sound freuency).
public int noteDuration; //Shape of note.
public string noteShape;
public MusicNote(int iPitch, int iNoteDuration)
: base()
{
pitch = iPitch;
noteDuration = iNoteDuration;
Size = new Size(40, 40);
}
public void ShowNote()
{ if (this.noteDuration == 1) noteShape = "Quaver.png";
if (this.noteDuration == 4) noteShape = "Crotchet.png";
if (this.noteDuration == 7) noteShape = "minim.png";
if (this.noteDuration == 10) noteShape = "DotMin.png";
if (this.noteDuration == 12) noteShape = "SemiBreve.png";
this.BackgroundImage = Image.FromFile(noteShape);
this.BackColor = Color.Transparent;
Location = new Point((pitch * 40) - 40, 100);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
public void PlaySound()
{
sp.SoundLocation = this.pitch + ".wav";
sp.Play();
}
public void StopSound()
{
sp.SoundLocation = this.pitch + ".wav";
sp.Stop();
}
public void Play()
{
sp.SoundLocation = this.pitch + ".wav";
sp.Play();
//Time to play the duration
tmr.Interval = noteDuration;
tmr.Start();
tmr.Tick += new System.EventHandler(ClockTick);
}
void ClockTick(object sender, EventArgs e)
{
sp.Stop();
tmr.Stop();
}
}
}
This is the code for the underlying control..the music staff
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace Simpe_Piano_new
{
public class MusicStaff: Panel
{
Pen myPen;
Graphics g;
public MusicStaff()
{
this.Size = new Size(1000, 150);
this.Location = new Point(0, 0);
this.BackColor = Color.Transparent;
this.Paint += new PaintEventHandler(DrawLines);
}
private void DrawLines(object sender, PaintEventArgs pea)
{
myPen = new Pen(Color.Black, 1);
g = this.CreateGraphics();
for (int i = 1; i < 6; i++)
{
g.DrawLine(myPen, 0, (this.Height / 6) * i, this.Width, (this.Height / 6) * i);
}
}
}
}
I have found that C# does not handle transparency really well...
Any help would be greatly appreciated..
add the top control "MusicNote" in the children of the underlying control "MusicStaff"
something like that after -Initializing all components-
// mStaff: the MusicStaff object
// mNote: the MusicNote object
mStaff.Children.Add(mNote);
in old scenario, the form is the parent of both of them, so they display the form background in any transparent area
after modifying the parent of the "MusicNote", it displays the "MusicStaff" background in the transparent area
I hope that help!
Two mistakes. PictureBox supports transparent images well, as long as you set its BackColor property to Color.Transparent. Which you did for the MusicStaff but not for the MusicNote. Layered transparency does not work, you don't need MusicStaff to be transparent, just the picture boxes.
This kind of transparency is simulated by asking the Parent to paint itself into the control to provide the background pixels. Which is your second mistake, you use CreateGraphics() in your DrawLines() method. Which draws directly to the screen, not the control surface. You must use pea.Graphics here.
Do note that the value-add you get from using PictureBox is a very low one. Controls are expensive and you'll easily burn up hundreds of them to display a sheet of music. You'll notice, it will become slow to paint itself. You avoid this by having MusicStaff just paint the notes itself, using Graphics.DrawImage() gets the job done. Transparency effects are now much simpler as well, just layers of paint. Which is the way WPF does it. The only inconvenience you'll have to deal with is that mouse hit testing isn't as simple anymore, you need to map the panel's MouseDown event's coordinates to a note. Just keep a List that keeps track where every note is displayed. You'll use that for painting as well as mouse hit testing.
I have a simple form with a Label1 on the form. I want a timer that will every 10 seconds update the label and change the value to increase by one or to whatever text I want to place in the text box.
This is the code that I am using. I have been able to have it flash up a message box and then to update the value in the text of the message box but the form will not appear unless I end the timer. I want the form to display and update a label1.Text fields value when the timer is called.
Ignore the graphics portion of the code.
Program Code:
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;
using System.Threading;
namespace DDHBindingExcelSheet
{
public partial class Form4 : Form
{
static System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
static int alarmCounter = 1;
static bool exitFlag = false;
public Form4()
{
InitializeComponent();
}
private void Form4_Load(object sender, EventArgs e)
{
time();
}
private void TimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
myTimer.Stop();
// Displays a message box asking whether to continue running the timer.
// if (MessageBox.Show("Continue running?", "Count is: " + alarmCounter,
// MessageBoxButtons.YesNo) == DialogResult.Yes)
// {
// Restarts the timer and increments the counter.
alarmCounter += 1;
l.Text = alarmCounter.ToString(); ;
myTimer.Enabled = true;
// }
// else
// {
// Stops the timer.
// exitFlag = true;
// }
}
private void time()
{
/*Adds the event and the event handler for the method that will
process the timer event to the timer.*/
myTimer.Tick += new EventHandler(TimerEventProcessor);
// Sets the timer interval to 5 seconds.
myTimer.Interval = 5000;
myTimer.Start();
// Runs the timer, and raises the event.
while (exitFlag == false)
{
// Processes all the events in the queue.
Application.DoEvents();
}
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g;
Pen myPen = new Pen(Color.Black, 2);
Point sp = new Point(73, 73);//starting point sp
Point ep = new Point(250, 240);//ending point ep
g = this.CreateGraphics();//tells compiler that we are going to draw on this very form
g.DrawLine(myPen, sp, ep);
myPen.Width = 1;
myPen.Color = Color.Red;
g.DrawRectangle(myPen, 240, 230, 25, 25);
myPen.Width = 1;
myPen.Color = Color.Black;
g.DrawLine(myPen, sp, ep);
g.DrawEllipse(myPen, 0, 0, 500, 500);//you can see here we use 30,30 same width and height to draw a circle if they were different an ellipse would be drawn where as x and y are the upper right coordinates of a rectangle bounding this circle.
g.DrawEllipse(myPen, 250, 240, 5, 5);
}
}
}
Simply do not EVER call Application.DoEvents()
Ïf you remove the following the program will work as intended:
// Runs the timer, and raises the event.
while (exitFlag == false)
{
// Processes all the events in the queue.
Application.DoEvents();
}
I'm new to C# programming and trying to write an application which is part of my final thesis.
I have a microprocessor that continuously send data from a sensor to my computer via serial port. All I want is to plotting this data using Zedgraph.
The problem is that the graph got too much delay and time lag. It seems the problem happens because I continuously update the whole graph at a very high rate.
I have stucked on this problem in a week and still dont find out a solution. I'll be more than happy if someone can help me out.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ZedGraph;
using System.IO.Ports;
using System.Threading;
namespace DynamicData
{
public partial class Form1 : Form
{
private SerialPort port;
private string buffer = "";
private void connect()
{
port = new SerialPort("COM8", 115200, Parity.None, 8, StopBits.One);
port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
if (!port.IsOpen) port.Open();
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e )
{
connect();
GraphPane myPane = zedGraphControl1.GraphPane;
RollingPointPairList list = new RollingPointPairList(500);
LineItem curve = myPane.AddCurve( "Sensor", list, Color.Blue, SymbolType.None );
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = 10;
myPane.YAxis.Scale.Min = 0;
myPane.YAxis.Scale.Max = 300;
myPane.XAxis.Scale.MinorStep = 0.5;
myPane.XAxis.Scale.MajorStep = 1;
zedGraphControl1.AxisChange();
}
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//sample data: ;100*100000:
//sampling rate ~100Hz
buffer += port.ReadExisting();
//flush incomplete package
while (buffer[0] != ';')
{
buffer = buffer.Remove(0, 1);
if (buffer.Length < 1) break;
}
//got a complete package, go to data handling
while (buffer.Contains(":"))
{
DataHandling();
}
}
private void DataHandling()
{
string[] nameArray = buffer.Split(new[] { ";", ":", "*" }, StringSplitOptions.RemoveEmptyEntries);
//plot sensor data vs. time
draw(Convert.ToInt32(nameArray[0]), Convert.ToInt32(nameArray[1]));
//remove handled package in buffer
var index = buffer.IndexOf(":");
buffer = buffer.Remove(0, index + 1);
}
double time = 0;
private void draw(int sensor, int t)
{
//convert tick to sec (uP clock rate = 16MHZ)
time = time + (t / 16000000.0);
// Get the first CurveItem in the graph
LineItem curve = zedGraphControl1.GraphPane.CurveList[0] as LineItem;
// Get the PointPairList
IPointListEdit list = curve.Points as IPointListEdit;
list.Add(time, sensor);
//Keep the X scale at a rolling 10 second interval, with one
//major step between the max X value and the end of the axis
Scale xScale = zedGraphControl1.GraphPane.XAxis.Scale;
if (time > xScale.Max - xScale.MajorStep)
{
xScale.Max = time + xScale.MajorStep;
xScale.Min = xScale.Max - 10.0;
}
//Display sensor data
this.Invoke(new Action(() => { textBox1.Text = byte1.ToString(); }));
axisChangeZedGraph(zedGraphControl1);
}
delegate void axisChangeZedGraphCallBack(ZedGraphControl zg);
private void axisChangeZedGraph(ZedGraphControl zg)
{
if (zg.InvokeRequired)
{
axisChangeZedGraphCallBack ad = new axisChangeZedGraphCallBack(axisChangeZedGraph);
zg.Invoke(ad, new object[] { zg });
}
else
{
// zg.AxisChange();
zg.Invalidate();
zg.Refresh();
}
}
}
}
Thank you for reading!
The problem is that you call invalidate with every point you draw. That produces a very high processor load. I'm working on a very similar project, USB device, realtime plotting data. I use a separate thread for data acquisition. This thread creates an event, every time it receives a datapacket. The data is put into a queue. The graph is updated with a timer. You find an example here. In this code the graph is updated every 50 ms, it is not really necessary to draw faster. In the timertick I check the size of the Queue and draw more or less points and then call invalidate. I don`t know if this is a good solution(just 6month experience in C#), but it works quite well with 7500 points. You should try to use a timer for the refresh of the graph first.
1). Create and format the the curve only once. store it at the module level.
2). When adding a point use curve.AddPoint(); then zg.Refresh();
//this is just to add a point to the plot, the curve object should have already been created
private void draw(int sensor, int t)
{
//convert tick to sec (uP clock rate = 16MHZ)
time = time + (t / 16000000.0);
//curve should be a module-level variable already set up with proper formatting,
//just no points yet
curve.AddPoint(time, sensor);
//Display sensor data
this.Invoke(new Action(() => { textBox1.Text = byte1.ToString(); }));
zg.AxisChange();
zg.Refresh();
}
How can i execute the a particluar loop for specified time
Timeinsecond = 600
int time = 0;
while (Timeinsecond > time)
{
// do something here
}
How can i set the time varaible here, if i can use the Timer object start and stop method it doesnot return me time in seconds
Regards
NewDev
May be the following will help:
Stopwatch s = new Stopwatch();
s.Start();
while (s.Elapsed < TimeSpan.FromSeconds(600))
{
//
}
s.Stop();
If you want ease of use:
If you don't have strong accuracy requirements (true millisecond level accuracy - such as writing a high frames per second video game, or similar real-time simulation), then you can simply use the System.DateTime structure:
// Could use DateTime.Now, but we don't care about time zones - just elapsed time
// Also, UtcNow has slightly better performance
var startTime = DateTime.UtcNow;
while(DateTime.UtcNow - startTime < TimeSpan.FromMinutes(10))
{
// Execute your loop here...
}
Change TimeSpan.FromMinutes to be whatever period of time you require, seconds, minutes, etc.
In the case of calling something like a web service, displaying something to the user for a short amount of time, or checking files on disk, I'd use this exclusively.
If you want higher accuracy:
look to the Stopwatch class, and check the Elapsed member. It is slightly harder to use, because you have to start it, and it has some bugs which will cause it to sometimes go negative, but it is useful if you need true millisecond-level accuracy.
To use it:
var stopwatch = new Stopwatch();
stopwatch.Start();
while(stopwatch.Elapsed < TimeSpan.FromSeconds(5))
{
// Execute your loop here...
}
Create a function for starting, stopping, and elapsed time as follows:
Class CustomTimer
{
private DateTime startTime;
private DateTime stopTime;
private bool running = false;
public void Start()
{
this.startTime = DateTime.Now;
this.running = true;
}
public void Stop()
{
this.stopTime = DateTime.Now;
this.running = false;
}
//this will return time elapsed in seconds
public double GetElapsedTimeSecs()
{
TimeSpan interval;
if (running)
interval = DateTime.Now - startTime;
else
interval = stopTime - startTime;
return interval.TotalSeconds;
}
}
Now within your foreach loop do the following:
CustomTimer ct = new CustomTimer();
ct.Start();
// put your code here
ct.Stop();
//timeinsecond variable will be set to time seconds for your execution.
double timeinseconds=ct.GetElapsedTime();
use Timers in c#
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
It's ugly .... but you could try this:
DateTime currentTime = DateTime.Now;
DateTime future = currentTime.AddSeconds(5);
while (future > currentTime)
{
// Do something here ....
currentTime = DateTime.Now;
// future = currentTime.AddSeconds(5);
}
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Accord.Video.FFMPEG;
namespace TimerScratchPad
{
public partial class Form1 : Form
{
VideoFileWriter writer = new VideoFileWriter();
int second = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
writer.VideoCodec = VideoCodec.H264;
writer.Width = Screen.PrimaryScreen.Bounds.Width;
writer.Height = Screen.PrimaryScreen.Bounds.Height;
writer.BitRate = 1000000;
writer.Open("D:/DemoVideo.mp4");
RecordTimer.Interval = 40;
RecordTimer.Start();
}
private void RecordTimer_Tick(object sender, EventArgs e)
{
Rectangle bounds = Screen.GetBounds(Point.Empty);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
writer.WriteVideoFrame(bitmap);
}
textBox1.Text = RecordTimer.ToString();
second ++;
if(second > 1500)
{
RecordTimer.Stop();
RecordTimer.Dispose();
writer.Close();
writer.Dispose();
}
}
}
}
Instead of such an expensive operation I'd recommend this: It's nasty but it's better to sit than running for doing nothing heating the cpu unnecesarily, the question is may be academic.
using System.Threading;
Thread.Sleep(600000);