I'm doing a small project where my vc# application needs to include a text scroller / news ticker. Application will be running on 30+ screens showing off internal ad production at my workplace.
I've been googling and testing for a couple of months now but has yet to find / create a good solution where the movement is smooth and not choppy.
So my question is: is it possible to create perfect smooth scroll motion in c# or do I need to go about it some other way?
The code I'm using at the moment, part of a sample I edited, is running almost smooth except it seems to lag every 100 ms or so.
Here is the code I'm using:
namespace ScrollDemo1
{
public partial class NewsTicker : Panel
{
private Timer mScroller;
private int mOffset;
private string mText;
private Size mPixels;
private Bitmap mBuffer;
public NewsTicker()
{
mScroller = new Timer();
mScroller.Interval = 1;
mScroller.Enabled = false;
mScroller.Tick += DoScroll;
}
[Browsable(true)]
public override string Text
{
get { return mText; }
set
{
mText = value;
mScroller.Enabled = mText.Length > 0;
mPixels = TextRenderer.MeasureText(mText, this.Font);
mOffset = this.Width;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
private void DoScroll(object sender, EventArgs e)
{
mOffset -= 1;
if (mOffset < -mPixels.Width) mOffset = this.Width;
Invalidate();
Update();
}
protected override void OnPaint(PaintEventArgs e)
{
if (mBuffer == null || mBuffer.Width != this.Width || mBuffer.Height != this.Height)
mBuffer = new Bitmap(this.Width, this.Height);
Graphics gr = Graphics.FromImage(mBuffer);
Brush bbr = new SolidBrush(this.BackColor);
Brush fbr = new SolidBrush(this.ForeColor);
Bitmap bmp = global::ScrollDemo1.Properties.Resources.text_bg1;
TextureBrush tb = new TextureBrush(bmp);
int iLoc = (this.Height / 2) - (mPixels.Height / 2);
//Console.WriteLine(iLoc.ToString());
//gr.FillRectangle(bbr, new Rectangle(0, 0, this.Width, this.Height));
gr.FillRectangle(tb, new Rectangle(0, 0, this.Width, this.Height));
gr.DrawString(mText, this.Font, fbr, mOffset, iLoc);
e.Graphics.DrawImage(mBuffer, 0, 0);
bbr.Dispose();
fbr.Dispose();
gr.Dispose();
}
}
}
I'd suggest you go with WPF. It has rich and easy to use support for animations and tends to be be very smooth since it is Direct3D based.
Related
So I have a small logo in the lower right corner of a Form that I want to fade in and out at a preset speed, about 6 seconds per fade. I have tried a few different methods but I can never get the picture to fade back in again once the first timer has finished. Here's my code for the the 2 timers and their respective tick methods.
EDIT The declarations for the timers included now.
Timer fade = new Timer();
Timer fade2 = new Timer();
fade.Interval = (200);
fade.Tick += new EventHandler(fade_Tick);
fade2.Interval = (200);
fade2.Tick += new EventHandler(fade_Tick_Two);
fade.Start();
private void fade_Tick(object sender, EventArgs e)
{
if (alpha < 255)
{
image = picboxPic.Image;
using (Graphics g = Graphics.FromImage(image))
{
Pen pen = new Pen(Color.FromArgb(alpha, 255, 255, 255), image.Width);
g.DrawLine(pen, -1, -1, image.Width, image.Height);
g.Save();
}
picboxPic.Image = image;
this.Invalidate();
alpha++;
}
else
{
fade.Stop();
fade2.Start();
}
}
private void fade_Tick_Two(object sender, EventArgs e)
{
if (alpha > 0)
{
image = picboxPic.Image;
using (Graphics g = Graphics.FromImage(image))
{
Pen pen = new Pen(Color.FromArgb(alpha, 255, 255, 255), image.Width);
g.DrawLine(pen, -1, -1, image.Width, image.Height);
g.Save();
}
picboxPic.Image = image;
this.Invalidate();
alpha--;
}
else
{
fade2.Stop();
fade.Start();
}
}
Any ideas as to why the second timer won't start? I've used breakpoints and the alpha level does reach 255 but then it doesn't trigger the second Tick event.
The method described in the link I quoted works for me:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
test();
}
System.Timers.Timer fade = new System.Timers.Timer(50);
System.Timers.Timer fade2 = new System.Timers.Timer(50);
Image originalImage = Image.FromFile(#"D:\kevin\Pictures\odds&Sods\kitchener.jpg");
int alpha = 100;
void test()
{
fade.Elapsed +=new System.Timers.ElapsedEventHandler(fade_Tick);
fade2.Elapsed+=new System.Timers.ElapsedEventHandler(fade_Tick_Two);
fade.Start();
}
delegate void timerDelegate(object sender, EventArgs e);
private void fade_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new timerDelegate(fade_Tick), sender, e);
return;
}
if (alpha >= 0)
{
picboxPic.Image = SetImgOpacity(originalImage, alphaToOpacity(alpha));
picboxPic.Invalidate();
alpha--;
}
if (alpha < 0)
{
alpha = 0;
fade.Stop();
fade2.Start();
}
}
private void fade_Tick_Two(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new timerDelegate(fade_Tick_Two), sender, e);
return;
}
if (alpha <= 100)
{
picboxPic.Image = SetImgOpacity(originalImage, alphaToOpacity(alpha));
picboxPic.Invalidate();
alpha++;
}
if (alpha > 100)
{
alpha = 100;
fade2.Stop();
fade.Start();
}
}
float alphaToOpacity(int alpha)
{
if (alpha == 0)
return 0.0f;
return (float)alpha / 100.0f;
}
//Setting the opacity of the image
public static Image SetImgOpacity(Image imgPic, float imgOpac)
{
Bitmap bmpPic = new Bitmap(imgPic.Width, imgPic.Height);
Graphics gfxPic = Graphics.FromImage(bmpPic);
ColorMatrix cmxPic = new ColorMatrix();
cmxPic.Matrix33 = imgOpac;
ImageAttributes iaPic = new ImageAttributes();
iaPic.SetColorMatrix(cmxPic, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
gfxPic.DrawImage(imgPic, new Rectangle(0, 0, bmpPic.Width, bmpPic.Height), 0, 0, imgPic.Width, imgPic.Height, GraphicsUnit.Pixel, iaPic);
gfxPic.Dispose();
return bmpPic;
}
}
The code is a bit crude and you will need to take care of the dispose when you close the form, but it fades up and and down and the rest can easily be taken care of - I also made it quicker for testing 'cos life's short :-)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Code running slow.. the code generates multiple circles have a center can be specified by first click then change the radius by moving the pointer to desired circle size then click to finish drawing the circle.. and repeat to draw another circle..I solved the problem by very simple step see the comments down
public partial class Form1 : Form
{
public class Seat
{
private string _SeatKey;
private Rectangle _SeatRectangle;
public Seat(string seatKey, Rectangle seatRectangle)
{
_SeatKey = seatKey;
_SeatRectangle = seatRectangle;
}
public string SeatKey
{
get { return _SeatKey; }
}
public Rectangle SeatRectangle
{
get { return _SeatRectangle; }
set { _SeatRectangle = value; }
}
}
List<Seat> _Seats = new List<Seat>();
List<Seat> _center = new List<Seat>();
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
for (int i = 0; i < 30; i = i + 1)
{
string mystring = "regctangle" + i.ToString();
_Seats.Add(new Seat(mystring, new Rectangle(50, 50, 50, 50)));
}
for (int i = 0; i < 30; i = i + 1)
{
string mystring = "center" + i.ToString();
_center.Add(new Seat(mystring, new Rectangle(50 , 50 , 3, 3)));
}
}
Bitmap background;
Graphics scG;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
int clikno = 1;
int xpos;
int ypos;
int clicknew = 0;
int radius=0;
int recH;
int recW;
int xcen;
int ycen;
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
clikno = clikno + 1;
clicknew = clicknew + 1;
}
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 1/2);
Pen mPen = new Pen(System.Drawing.Color.Black, 3);
scG.Clear(SystemColors.Control);
_Seats[clikno].SeatRectangle = new Rectangle(xpos, ypos, recH, recW);
_center[clikno].SeatRectangle = new Rectangle(xcen, ycen, 3, 3);
for (int i = 0; i < clikno+1; i = i + 1)
{
scG.DrawEllipse(myPen, _Seats[i].SeatRectangle);
scG.DrawEllipse(mPen, _center[i].SeatRectangle);
}
Refresh();//This what cause the code running slow
return background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
AutoSize = true;
clickCurrent = this.PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
Refresh();
clickCurrent = Point.Empty;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
double oradius = Math.Sqrt((Math.Pow(clickPrev.X - e.X, 2)) + (Math.Pow(clickPrev.Y - e.Y, 2)));
radius = Convert.ToInt32(oradius);
if (clicknew == 1)
{
recH = radius;
recW = radius;
xpos = clickPrev.X - recW / 2;
ypos = clickPrev.Y - recH / 2;
xcen = clickPrev.X - 3 / 2;
ycen = clickPrev.Y - 3 / 2;
Refresh();
}
if (clicknew == 2)
clicknew = 0;
Refresh();
}
}
The part that is so slow is your MouseMove.
Usually one checks for the left mousebutton to be pressed:
private void yourDrawingControl_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left) )
{
// draw stuff
}
}
If that is not a condition that applies you should check for having moved for at least more than one pixel:
Point oldLocation = Point.Empty;
private void yourDrawingControl_MouseMove(object sender, MouseEventArgs e)
{
int minStep = 3;
if ( (Math.Abs(oldLocation.X - e.X) + Math.Abs(oldLocation.Y - e.Y) > minStep) )
{
// draw stuff
}
oldLocation = e.Location;
}
Also: While the graphics is being built up, do not draw into a Bitmap which you then draw with DrawImage in your Paint event. Instead in the Paint event draw directly onto the surfce of the control from a List<yourDrawingshapeClass> !
Drawing hundreds of Rectangles is extremely fast compared to drawing even one of your Bitmaps..
Also: It looks as if you are drawing onto the Form? Better draw onto a dedicated control with just the right size; the natural choice is a PictureBox, which is made for this and is double-buffered out-of-the-box. Instead your enforce the form to refresh all it controls..
Finally: Even if you refuse to change your convoluted code to something well-proven, at least make it not call Refresh in the MouseMove unconditionally (in the last line), even if nothing at all has changed!
I'm in need of a way to make TextBox appear like a parallelogram but i can't figure out how to do so. I currently have this code:
private void IOBox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point cursor = PointToClient(Cursor.Position);
Point[] points = { cursor, new Point(cursor.X + 50, cursor.Y), new Point(cursor.X + 30, cursor.Y - 20),
new Point(cursor.X - 20, cursor.Y - 20) };
Pen pen = new Pen(SystemColors.MenuHighlight, 2);
g.DrawLines(pen, points);
}
But apparently it's not working. Either i misplaced/misused it or i'm not doing something right.
This is the method that i use to add it.
int IOCounter = 0;
private void inputOutput_Click(object sender, EventArgs e)
{
IOBox box = new IOBox();
box.Name = "IOBox" + IOCounter;
IOCounter++;
box.Location = PointToClient(Cursor.Position);
this.Controls.Add(box);
}
Any idea how i can fix it? IOBox is a UserControl made by me which contains a TextBox. Is that rightful to do?
If its possible, you should make your application using WPF. WPF is designed to do exactly what you are trying to do.
However, it can be done in WinForms, though not easily. You will need to make a new class that inherits the TextBox WinForm control. Here is an example that makes a TextBox look like a circle:
public class MyTextBox : TextBox
{
public MyTextBox() : base()
{
SetStyle(ControlStyles.UserPaint, true);
Multiline = true;
Width = 130;
Height = 119;
}
public override sealed bool Multiline
{
get { return base.Multiline; }
set { base.Multiline = value; }
}
protected override void OnPaintBackground(PaintEventArgs e)
{
var buttonPath = new System.Drawing.Drawing2D.GraphicsPath();
var newRectangle = ClientRectangle;
newRectangle.Inflate(-10, -10);
e.Graphics.DrawEllipse(System.Drawing.Pens.Black, newRectangle);
newRectangle.Inflate(1, 1);
buttonPath.AddEllipse(newRectangle);
Region = new System.Drawing.Region(buttonPath);
base.OnPaintBackground(e);
}
}
Keep in mind that you will still have to do other things, such as clipping the text, etc. But this should get you started.
I'd like to draw animation where the airplane crossing form from leftside to the right.
public partial class Form1 : Form
{
Bitmap sky, plane, background;
int currentX, currentY;
Random rndHeight;
Rectangle planeRect;
Graphics g;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
sky = new Bitmap("sky.jpg");
plane = new Bitmap("plane1.png");
int planeWidth = plane.Width; int planeHeight = plane.Height;
}
catch (Exception) { MessageBox.Show("No files!"); }
this.ClientSize = new System.Drawing.Size(sky.Width, sky.Height);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
background = new Bitmap(sky);
g = Graphics.FromImage(background);
rndHeight = new Random();
currentX = -plane.Width; currentY = rndHeight.Next(0, this.Height);
this.BackgroundImage = background;
timer1.Interval = 1;
timer1.Enabled = true;
timer1.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(sky, 0, 0);
e.Graphics.DrawImage(plane, planeRect);
}
private void timer1_Tick(object sender, EventArgs e)
{
g.DrawImage(sky, 0, 0);
planeRect.X = currentX; planeRect.Y = currentY; planeRect.Width = plane.Width; planeRect.Height = plane.Height;
g.DrawImage(plane, planeRect);
Rectangle myNewPlane = new Rectangle(planeRect.X - 10, planeRect.Y - 10, planeRect.Width + 20, planeRect.Height + 20);
this.Invalidate(myNewPlane);
if (currentX >= this.Width) currentX = -plane.Width; else currentX += 2;
currentY += rndHeight.Next(-2, 2);
}
}
This code works, but the Rectangle of plane flickers with the frequency of timer1.Interval. My question is: how can I avoid these flickers?
p.s.: background image resolution 1024x768; plane - 160x87. plane is transparent
You fix this by setting the DoubleBuffering style for your Form to remove the flicker, e.g.
DoubleBuffered = true;
You probably want to set a few more control styles as well for automatic double-buffering (after InitializeComponents)
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,true);
More information about automatic and manual double-buffering is on MSDN here
I have 3 pictures, each one has a colored circle in it. The 3 pictures are red, green and yellow.
I'm putting it in a PictureBox in a windows form. I want to switch these images from green to yelow to red or otherwise.
Is there anything I can make them fade to each other instead of switching them the normal way?
I know this could be done using flash/j-query easily, but I was wondering how far I can achieve .
Something similar in windows forms using normal windows forms functionality.
Note: I'm using .net framework 4 and windows forms.
See Transition of images in Windows Forms Picture box. There is a solution that transitions the images using a timer on this page.
Code from site:
public class BlendPanel : Panel
{
private Image mImg1;
private Image mImg2;
private float mBlend;
public BlendPanel()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
}
public Image Image1
{
get { return mImg1; }
set { mImg1 = value; Invalidate(); }
}
public Image Image2
{
get { return mImg2; }
set { mImg2 = value; Invalidate(); }
}
public float Blend
{
get { return mBlend; }
set { mBlend = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
if (mImg1 == null || mImg2 == null)
{
e.Graphics.FillRectangle(new SolidBrush(this.BackColor),
new Rectangle(0, 0, this.Width, this.Height));
}
else
{
Rectangle rc = new Rectangle(0, 0, this.Width, this.Height);
ColorMatrix cm = new ColorMatrix();
ImageAttributes ia = new ImageAttributes();
cm.Matrix33 = mBlend;
ia.SetColorMatrix(cm);
e.Graphics.DrawImage(mImg2, rc, 0, 0, mImg2.Width,
mImg2.Height, GraphicsUnit.Pixel, ia);
cm.Matrix33 = 1F - mBlend;
ia.SetColorMatrix(cm);
e.Graphics.DrawImage(mImg1, rc, 0, 0, mImg1.Width,
mImg1.Height, GraphicsUnit.Pixel, ia);
}
base.OnPaint(e);
}
}
Build your project. You can now drop a BlendPanel from the top of the toolbox onto your form. Here's a sample program that uses it:
namespace WindowsApplication1
{
public partial class Form1 : Form
{
private float mBlend;
private int mDir = 1;
public Form1()
{
InitializeComponent();
timer1.Interval = 30;
timer1.Tick += BlendTick;
blendPanel1.Image1 = Bitmap.FromFile(#"c:\temp\test1.bmp");
blendPanel1.Image2 = Bitmap.FromFile(#"c:\temp\test2.bmp");
timer1.Enabled = true;
}
private void BlendTick(object sender, EventArgs e)
{
mBlend += mDir * 0.02F;
if (mBlend < 0) { mBlend = 0; mDir = 1; }
if (mBlend > 1) { mBlend = 1; mDir = -1; }
blendPanel1.Blend = mBlend;
}
}
}
You'll need to modify the Bitmap.FromFile() calls. Build and run. You should see the displayed image smoothly morph from your first image to your second image without any flickering. Lots of ways to tweak the code, have fun.
I don't know if it is a good idea, but i would go for 2 image boxes, one to fade in, other to fade out, and change alpha when time passes.
Like what #Igoris suggested, You need to use two controls overlap each other, and you should define a timer so when you want to fade in or out, start the timer and in its tick decrease the Transparent of the first control and increase it on the second one... , the problem is that the ordinary controls does not support transparent by default. so you have to inherit it and apply transparent here is a custom TransparentPictureBox that inherited from PictureBox:
public class TransparentPictureBox : System.Windows.Forms.PictureBox
{
/// <summary>
/// Initialize new instance of this class.
/// </summary>
public TransparentPictureBox()
: base()
{
DoubleBuffered = true;
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.BackColor = Color.Transparent;
}
}