I have a very simple question i guess...for which i am not able to find an answer for. I am trying to add an autohide feature for a borderless WinForm which is located at (0,0) with a width of 150. I have the following code:
private int dx;
private void autohide()
{
for (dx = 0; dx > -150; dx--)
{
this.Width = dx;
Thread.Sleep(2);
}
}
Even after, using Thread.Sleep(x), the Form just snaps off to final Width without giving/having any effect of delay. I am trying to put a bit of effect on to it .
Please help...
The issue you are facing is because the window is not re-drawing itself at any point because your code doesn't exit the autohide() routine until dx is 150, so it will just have a delay before re-drawing in the final position.
You probably also want to change the position rather than the width.
The better option would be to start up a Timer which then changes the position each time it fires, which would cause the change to be animated:
private Timer t;
private int step = 1;
private void autohide()
{
t = new Timer();
t.Interval = 2;
t.Tick += T_Tick;
t.Start();
}
private void T_Tick(object sender, EventArgs e)
{
if (this.Location.X > 0 - this.Width)
{
this.Location = new Point(this.Location.X - step, this.Location.Y);
}
else
{
t.Stop();
}
}
Related
I want to make a moving label seem nicer and smoother than just reappearing the whole thing to the left after it has all gone out of panel width .For example label 'Hello' , as soon as 'lo' goes out of bounds in the right I want it to reappear on the left. Is there any possible solution to this ?
Here's the code I have for the label now .
private void timer2_Tick(object sender, EventArgs e)
{
label5.Location = new Point(label5.Location.X + 3, label5.Location.Y);
if (label5.Location.X > this.Width)
{
label5.Location = new Point(0 - label5.Width, label5.Location.Y);
}
}
Try this, using a Label (here, named lblMarquee and a System.Windows.Forms.Timer).
The scrolling time is regulated by both the Timer.Interval and a float Field (marqueeStep).
The Timer.Tick event just calls lblMarquee.Invalidate(), causing the Label control to repaint itself.
When the scrolling text, in relation to its current position, goes beyond the limits of the Label.ClientRectangle, the section of the text which is not visible anymore is painted at start of the Label.ClientArea:
System.Windows.Forms.Timer marqueeTimer = new System.Windows.Forms.Timer();
string marqueeText = string.Empty;
float marqueePosition = 0f;
float marqueeStep = 4f;
private void form1_Load(object sender, EventArgs e)
{
marqueeText = lblMarquee.Text;
lblMarquee.Text = string.Empty;
marqueeTimer.Tick += (s, ev) => { this.lblMarquee.Invalidate(); };
marqueeTimer.Interval = 100;
marqueeTimer.Start();
}
private void lblMarquee_Paint(object sender, PaintEventArgs e)
{
var marquee = sender as Label;
SizeF stringSize = e.Graphics.MeasureString(marqueeText, marquee.Font, -1, marqueeFormat);
PointF stringLocation = new PointF(marqueePosition, (marquee.Height - stringSize.Height) / 2);
stringLength = marquee.ClientRectangle.Width - stringLocation.X;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, stringLocation, marqueeFormat);
if (marqueePosition >= marquee.ClientRectangle.Width) marqueePosition = 0f;
if (stringSize.Width + stringLocation.X > marquee.ClientRectangle.Width) {
PointF partialStringPos = new PointF(-stringLength, (marquee.Height - stringSize.Height) / 2);
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, partialStringPos, marqueeFormat);
}
marqueePosition += marqueeStep;
}
A couple of other implementations you might find useful:
How to follow the end of a text in a TextBox with no NoWrap
How to draw a string on two not adjacent areas
You need to have two label controls to do this, but it's not really that difficult. First, create a backup label and set it's properties to look like label5:
// A backup label for our scrolling label5
private Label label5_backup;
private void Form1_Load(object sender, EventArgs e)
{
label5.Text = "This is a scrolling label!";
// Set label5_backup to look like label5
label5_backup = new Label
{
Size = label5.Size,
Text = label5.Text,
Top = label5.Top,
Visible = false
};
Controls.Add(label5_backup);
timer2.Interval = 1;
timer2.Start();
}
Then, in the Tick event, as soon as our label5 starts to leave the client rectangle, set our backup label to the proper distance from the left of the form so that it starts to appear on the other side. And as soon as label5 is completely off the form, set it's location to match the backup label and then hide the backup label again.
Note that you can just set the Left property instead of creating a new Location point each time, which simplifies the code a little:
private void timer2_Tick(object sender, EventArgs e)
{
label5.Left++;
// If label5 starts to go off the right, show our backup on the left side of the form
if (label5.Right > ClientRectangle.Width)
{
label5_backup.Left = label5.Right - ClientRectangle.Width - label5.Width;
label5_backup.Visible = true;
}
// If label5 is all the way off the form now, set it's location to match the backup
if (label5.Left > ClientRectangle.Width)
{
label5.Location = label5_backup.Location;
label5_backup.Visible = false;
}
}
Also, if you want to make the scrolling smoother, only increment the Left by 1 each time and reduce the timer2.Interval to a third of what it was before (unless it's already at 1).
I am making my own little custom message box, essentially its just a small box showing up with a message for X amount of time, and then fades away for Y duration.
What is happening tho is that the fade away takes double as long as its supposed to and I cannot figure out why. Can someone look at my code and spot why its taking double the amount of time to fade the form away than it is expected?
//Initiate and set up the message bubble.
public static void InitiateBubble(String displayText, Double showTime = 1000, Double fadeTime = 2000) {
Bubble bubble = new Bubble(displayText);
bubble.showTime = showTime;
bubble.fadeTime = fadeTime;
bubble.Show();
bubble.showTimer = new Timer();
bubble.showTimer.Interval = (int)bubble.showTime;
bubble.showTimer.Tick += bubble.startFadeAway;
bubble.showTimer.Start();
}
//Leaves some time on screen before starting to fade away
private void startFadeAway(object sender, EventArgs e) {
showTimer.Stop();
fadeAwayTimer = new Timer();
fadeAwayTimer.Interval = 10;
fadeAwayTimer.Tick += fadeAway;
fadeAwayTimer.Start();
}
//slowly fades the contorle away until it disapears.
private void fadeAway(object sender, EventArgs e) {
double opacity = Opacity;
opacity -= (10 / fadeTime);
if (opacity < 0) {
Close();
}
else {
Opacity = opacity;
}
}
If the user sets the fade interval to 1 second (1000 milliseconds), and we set the timer interval to 1/10th of a second (100 milliseconds), then we need to fade the opacity by 10% every interval (since the interval is triggered 10 times per second). So we would set Opacity -= .1 on each iteration.
If the user sets the fade interval to 2 seconds (2000 milliseconds), and we still have the timer interval set to 1/10th of a second, then we need to fade the opacity by only 5% every interval, so we would set Opacity -= .05 on each iteration.
Seeing this relationship, we can discover that:
var amountToReduceOpacity = 1.0 / fadeTime * interval;
Note: as γηράσκω δ' αεί πολλά διδασκόμε mentioned above, the resolution on the winform timer is around 17 milliseconds, so if we set it to less than this, the fade will slow down dramatically because we will have calculated the rate for a very fast timer (meaning that we won't fade very much each iteration), but it will execute more slowly. On my machine, setting it to 50 looks just fine.
Now we can use this formula to always fade the form by the correct amount each interval. Here's a sample Form that does basically what you're doing above (note that I dropped a label and two timers on the form, and named them: lblDisplay, showTimer, and fadeTimer):
public partial class Bubble : Form
{
private readonly double amountToReduceOpacity;
private readonly int fadeInterval = 50;
// Custom form constructor takes in all three required settings
public Bubble(string displayText, int showTime, int fadeTime)
{
InitializeComponent();
lblDisplay.AutoSize = true;
lblDisplay.Text = displayText;
lblDisplay.Left = ClientRectangle.Width / 2 - lblDisplay.Width / 2;
lblDisplay.Top = ClientRectangle.Height / 2 - lblDisplay.Height / 2;
showTimer.Interval = showTime;
fadeTimer.Interval = fadeInterval;
amountToReduceOpacity = 1.0 / fadeTime * fadeInterval;
}
// The Shown event starts the first timer
private void Bubble_Shown(object sender, EventArgs e)
{
showTimer.Start();
}
// The shownTimer starts the fadeTimer
private void showTimer_Tick(object sender, EventArgs e)
{
showTimer.Stop();
BackColor = Color.Red; // Just so we see when the fade starts
fadeTimer.Start();
}
// The fade timer reduces opacity on each iteration until it's zero
private void fadeTimer_Tick(object sender, EventArgs e)
{
Opacity -= amountToReduceOpacity;
if (Opacity <= 0) Close();
}
}
Then on the client side we can just do something like:
private void button1_Click(object sender, EventArgs e)
{
Bubble bubble = new Bubble("Help me, I'm Fading!", 1000, 2000);
bubble.Show();
}
I'm trying to make a breakout game for an assignment in windows form and I've made games before, but I've just not used winforms before. I've found things that are supposed to help like OnPaint() (which you're supposed to override), DoubleBuffered andInvalidate`. But I'm just struggling applying it to my code
Here's what I have:
int xSpeed, ySpeed;
Graphics display;
Brush brush;
Rectangle ballRect;
public Form1()
{
InitializeComponent();
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
display = this.CreateGraphics();
brush = new SolidBrush(Color.Red);
}
private void timer1_Tick(object sender, EventArgs e)
{
DoubleBuffered = true;
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X >= 469)
xSpeed = -xSpeed;
if (ballRect.Y >= 457)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
display.Clear(Color.White);
display.FillEllipse(brush, ballRect);
}
I'm drawing the ball in Update method (timer1_tick), but I feel like I shouldn't be.
Thanks :)
You can create your custom Control or Form and:
In constructor Set styles for flicker free painting OptimizedDoubleBuffer, UserPaint, AllPaintingInWmPaint
In constructor Set style for redraw if the size changes ResizeRedraw
In Tick event just update position of ball and then Invalidate the control
override OnPaint method of your control and put the paint logic there
Also you should use properties for speed or ball size, to be able to invalidate the control if those values changed. (I didn't implement properties in below code.) Also you can increase the interval a little.
For example:
public partial class CustomControl1 : Control
{
public CustomControl1()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
timer1.Enabled = true;
timer1.Interval = 1;
xSpeed = 5;
ySpeed = 5;
ballRect = new Rectangle(10, 10, 20, 20);
brush = new SolidBrush(Color.Red);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.FillEllipse(brush, ballRect);
}
int xSpeed, ySpeed;
Brush brush;
Rectangle ballRect;
private void timer1_Tick(object sender, EventArgs e)
{
ballRect.X += xSpeed;
ballRect.Y += ySpeed;
if (ballRect.X + ballRect.Width >= this.Width)
xSpeed = -xSpeed;
if (ballRect.Y + ballRect.Height >= this.Height)
ySpeed = -ySpeed;
if (ballRect.X <= 0)
xSpeed = -xSpeed;
if (ballRect.Y <= 0)
ySpeed = -ySpeed;
this.Invalidate();
}
}
The flickering is happening because you are drawing directly to the display. First you clear the display, which the user will see for a split second, then you draw a circle over it, which is only displayed for a short time before the process repeats. That "overdrawing" is where the flicker is coming from.
One method to get rid of it is to do all your drawing to a memory bitmap and, once complete, move the entire bitmap to the display.
Once other issue I see with your code is that you create a Graphics instance in the constructor and keep it around for the life of the program. That's a pattern that you should avoid. Instead you should create a new Graphics object, preferably in a using statement for each "frame". That will make sure that everything is getting cleaned up properly.
I want to make a picture appear on my form, a certain number of seconds after it loads, and then have that picture move in a controlled manner within the form boundaries. I'd appreciate a code example that will get me started with timed events.
public partial class Form1 : Form
{
int horiz, vert, step;
public Form1()
{
InitializeComponent();
}
private void timer1_Tick_1(object sender, EventArgs e)
{
//image is moved at each interval of the timer
goblin.Left = goblin.Left + (horiz * step);
goblin.Top = goblin.Top + (vert * step);
// if goblin has hit the RHS edge, if so change direction left
if ((goblin.Left + goblin.Width) >= (Form1.ActiveForm.Width - step))
horiz = -1;
// if goblin has hit the LHS edge, if so change direction right
if (goblin.Left <= step)
horiz = 1;
// if goblin has hit the bottom edge, if so change direction upwards
if ((goblin.Top + goblin.Height) >= (Form1.ActiveForm.Height - step))
vert = -1;
// if goblin has hit the top edge, if so change direction downwards
if (goblin.Top < step)
vert = 1;
}
private void Form1_Load_1(object sender, EventArgs e)
{
//Soon as the forms loads activate the goblin to start moving
//set the intial direction
horiz = 1; //start going right
vert = 1; //start going down
step = 5; //moves goblin 5 pixels
timer1.Enabled = true;
}
}
}
The easiest solution based on what you've shown us so far is to use the same timer you are using already and essentially skip a few ticks. Let's assume that your current timer is happening at 100ms which is 10 timers per second (10hz)
If you want to delay this activity by 5 seconds, you need to skip 5 * 10 (50) of the first ticks. Create a new integer member variable to store how many ticks you've processed:
private int ticks = 0;
Each time the timer expires/ticks do this first:
ticks++;
if (ticks < 50) {
// Don't do anything just skip
return;
}
You can provide second 'temporary' timer (timer2)
private void Form1_Load_1(object sender, EventArgs e)
{
//Soon as the forms loads activate the goblin to start moving
//set the intial direction
horiz = 1; //start going right
vert = 1; //start going down
step = 5; //moves goblin 5 pixels
timer1.Tick += timer1_Tick_1;
// temporary timer
Timer timer2 = new Timer();
timer2.Interval = 5000;
timer2.Tick += delegate
{
// activate goblin timer
timer1.Enabled = true;
// deactivate 5s temp timer
timer2.Enabled = false;
timer2.Dispose();
};
timer2.Enabled = true;
}
At the top of Form1 i did:
private IntPtr ID;
private int counter = 0;
In the constructor i did:
ID = this.Handle;
timer2.Enabled = true;
Then in timer2 tick event i did:
private void timer2_Tick(object sender, EventArgs e)
{
if (counter <= Screen.PrimaryScreen.Bounds.Right)
MoveWindow(ID, counter++, 0, this.Width, this.Height, true);
else
counter = 0;
}
But the form start to move from the top left corner at 0,0 to the right.
I want the form will start to move from the center of the screen to the left untill it hit the left border/bound and stop and stay there.
How can i do it ?
I found now how to make it to move to the left and stop on the left border/bound:
private void timer2_Tick(object sender, EventArgs e)
{
if (counter >= Screen.PrimaryScreen.Bounds.Left)
MoveWindow(ID, counter--, 0, this.Width, this.Height, true);
else
counter = 0;
}
But how do i make that it will start to move from the middle/center of the screen ?
I did in the designer change the property: StartPosition to CenterScreen
But the form start to move from the top left corner 0,0
There is a better solution for you, just use the protected method CenterToScreen() like this:
this.CenterToScreen();
This is the answer.
x = Screen.PrimaryScreen.Bounds.Bottom - this.Width * 2;
y = Screen.PrimaryScreen.Bounds.Bottom - this.Height * 2;
counter = x;
Then in the timer tick event:
private void timer2_Tick(object sender, EventArgs e)
{
if (counter >= Screen.PrimaryScreen.Bounds.Left)
MoveWindow(ID, counter--, y, this.Width, this.Height, true);
else
counter = 0;
}
Before it the counter-- was set to 0. And instead y it was 0 too.
So it's 0,0 this is the location.
Now counter and y start location is the middle of the screen.
Thanks.
You could set the StartPosition property of your main WinForm to CenterScreen:
Form1.StartPosition = FormStartPosition.CenterScreen;
Then if you want it to appear somehow on a different position relative to this screen center, you can play with the Top and Left properties to add or subtract the required number of pixels.
if you need to know the screen bounds:
System.Windows.Forms.Screen.PrimaryScreen.Bounds
or, taking into account the task bar:
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea
...or some variant