How can I delay an animation without using System.Threading.Thread.Sleep() - c#

I want to move my text up, but if I use System.Threading.Thread.Sleep() my app gets stuck. I think that the using a Timer is a good way to solve it but pls show me how. I was trying to use Animate() also, but I didn't solve it by this way.
for (int i = 0; i < 30; i+=2)
{
Brush snizovaniViditelnosti = new SolidBrush(Color.FromArgb(0+i*8, 0+i*8,0+i*8));
g.DrawString("+1", fontPridaniMaterialu, snizovaniViditelnosti, MousePosition.X, MousePosition.Y - i);
System.Threading.Thread.Sleep(30);
//ImageAnimator.Animate()
Timer d = new Timer();
d.Interval = 55;
Refresh();
}
It's suppose to work that I click on some button and then appears text - "+1" and it will be moving up with reducing opacity. Finally it will disappear.

Grab Timer and Button from toolbox.
Then select timer and go to events section in properties window. Double click Tick event. Apply your logic for moving text.
For button you need to use click event.
Sample code:
private void timer1_Tick (object sender, EventArgs e)
{
button1.Location = new Point (button1.Location.X + 1, button1.Location.Y);
}
private void button1_Click (object sender, EventArgs e)
{
timer1.Start ();
}

You will have to create a Timer out of your for loop and replace the loop by a Tick event. At the moment you are re-creating the Timer in every loop iteration. Put it as a component to your control, like this:
// Timer Interval is set to 0,5 second
private Timer _timer = new Timer { Interval = 500 };
And adding also the following fields to your control for
private int _index = 0;
private int _maxIndex = 30;
After this adding a delegate to the Tick event, which will moving up your text a ttle bit on every tick.
this._timer.Tick += delegate
{
if (this._index < this._maxIndex)
{
var alphaValue = 255 - this._index * 8;
Brush snizovaniViditelnosti = new SolidBrush(Color.FromArgb(alphaValue, 255, 255, 255));
g.DrawString("+1", fontPridaniMaterialu, snizovaniViditelnosti, MousePosition.X, MousePosition.Y - this._index);
Refresh();
this._index++;
}
else
{
this._timer.Stop();
}
};
If you only want to reduce opacity, reduce the alpha value and leave the color - as shown in the example above.
And wire this to your Button click event
private void Button_Click(object sender, EventArgs e)
{
this._timer.Start();
}
Hint: This is a quick solution for only one item. If you want to do this for more than one item you may add a class to your code containing the text, the Timer and the current and maxIndex.
I guess you are using winforms.
To avoid flickering while re-drawing your UI. You should activate double buffering.
See more information about Handling and Raising Events
As #apocalypse suggested in his answer. It will be better to setup a fix start location for your text to move up.

Related

WinForm button background image updates only every other run

I want a blinking LED -- alternately a lighted, then a dark image in a PictureBox -- to appear during a run (that I click a button to start). The image lights when the run starts, and goes dark when it the run finishes. That always works.
This code:
this.timer.SynchronizingObject = this;
this.timer.Interval = 250;
this.timer.Elapsed += (s, ea) =>
{
this.ledLit = !this.ledLit;
ShowInLog(this.ledLit ? "/" : "\\");
this.picMarking.BackgroundImage = this.ledLit ? this.imageStopped : this.imageRunning;
this.picMarking.Refresh();
};
works great to show a blinking LED image during the run...every other run.
On every even-numbered invocation, the display of alternating slashes shows that the timer is working, but the background image does not update (except perhaps a rare flicker).
Why? How do I make it to work on every invocation?
Here's some quick code I put together for your "blinking" effect:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Blinker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 250;
timer1.Tick += timer1_Tick;
timer1.Start();
}
void timer1_Tick(object sender, EventArgs e)
{
Console.WriteLine("Tick");
pictureBox1.BackColor = (pictureBox1.BackColor == System.Drawing.Color.Red) ? System.Drawing.Color.Black : System.Drawing.Color.Red;
}
}
}
I've used event handlers for the Tick event on the timer to trigger the blinking. You can adjust the image like I have with the BackColor property. It's a quick and dirty approach, but it gets the blinking effect achieved.
I found the answer to my problem here: Custom event handler is repeating itself...
My problem is that I subscribed to the tick event each time, rather than only once.

WP7, C#, Increase slider value by holding a button

I have to increase the slider control value while holding a button.
As long as I am holding the button the slider has to keep increasing.
For example, the scenario is typical with volume control.
I have a slider for volume and a button Increase for volume.
Now as long as I keep holding the Increase button, the volume (marker) in the slider should keep increasing continuously till I release the button.
What I have achieved is changing the value of the slider on individual click events on the button.
Kindly give your suggestions on how I can achieve this.
According to the book there should be RepeatButton. In this case it will perfectly suit your needs. Try to avoid Thread sleeps. Freezes are not pretty good thing. It's one of first candidates for refactoring.
public bool Ok = false;
public void Do()
{
while (Ok)
{
this.Text += ".";
System.Threading.Thread.Sleep(100);
Application.DoEvents();
//I added dots to the form text , You do your own mission
}
}
private void btnLouder_MouseDown(object sender, MouseEventArgs e)
{
Ok = true;
Do();
}
private void btnLouder_MouseUp(object sender, MouseEventArgs e)
{
Ok = false;
}
Add a TrackBar control to the form and then
Replace the code in While block with this one, it does exactly what you want :
if(trackBar1.Value < trackBar1.Maximum)
trackBar1.Value += 1;

Track Bar Only fire event on final value not ever time value changes

I am working on a pretty basic C# visual studio forms application but am having some issue getting the track bar to act as I want it to so hoping someone in the community might have a solution for this.
What I have is a pretty basic application with the main part being a track bar with a value of 0 to 100. The user sets the value of the track to represent "the amount of work to perform" at which point the program reaches out to some devices and tells them to do "x" amount of work (x being the value of the trackbar). So what I do is use the track bars scroll event to catch when the track bars value has changed and inside the handler call out to the devices and tells them how much work to do.
My issue is that my event handler is called for each value between where the track bar currently resides and where ever it ends. So if it is slid from 10 to 30, my event handler is called 20 times which means I am reaching out to my devices and telling them to run at values I don't even want them to run at. Is there someway only to event when scroll has stopped happening so you can check the final value?
Just check a variable, if the user clicked the track bar. If so, delay the output.
bool clicked = false;
trackBar1.Scroll += (s,
e) =>
{
if (clicked)
return;
Console.WriteLine(trackBar1.Value);
};
trackBar1.MouseDown += (s,
e) =>
{
clicked = true;
};
trackBar1.MouseUp += (s,
e) =>
{
if (!clicked)
return;
clicked = false;
Console.WriteLine(trackBar1.Value);
};
For the problem #roken mentioned, you can set LargeChange and SmallChange to 0.
Try the MouseCaptureChanged event - that is the best for this task
A user could also move the track bar multiple times in a short period of time, or click on the track multiple times to increment the thumb over instead of dragging the thumb. All being additional cases where the value that registers at the end of a "thumb move" is not really the final value your user desires.
Sounds like you need a button to confirm the change, which would then capture the current value of the trackbar and send it off to your devices.
Try this with the trackbar_valuechanged event handler:
trackbar_valuechanged(s,e) {
if(trackbar.value == 10){
//Do whatever you want
} else{
//Do nothing or something else
}
}
I found a fairly reliable way to do this is to use a timer hooked up in the trackbar.Scroll event:
private Timer _scrollingTimer = null;
private void trackbar_Scroll(object sender, EventArgs e)
{
if (_scrollingTimer == null)
{
// Will tick every 500ms (change as required)
_scrollingTimer = new Timer()
{
Enabled = false,
Interval = 500,
Tag = (sender as TrackBar).Value
};
_scrollingTimer.Tick += (s, ea) =>
{
// check to see if the value has changed since we last ticked
if (trackBar.Value == (int)_scrollingTimer.Tag)
{
// scrolling has stopped so we are good to go ahead and do stuff
_scrollingTimer.Stop();
// Do Stuff Here . . .
_scrollingTimer.Dispose();
_scrollingTimer = null;
}
else
{
// record the last value seen
_scrollingTimer.Tag = trackBar.Value;
}
};
_scrollingTimer.Start();
}
}
I had this problem just now as I'm implementing a built in video player and would like the user to be able to change the position of the video but I didn't want to overload the video playback API by sending it SetPosition calls for every tick the user passed on the way to his/her final destination.
This is my solution:
First, the arrow keys are a problem. You can try your best to handle the arrow keys via a timer or some other mechanism but I found it more pain than it is worth. So set the property SmallChange and LargeChange to 0 as #Matthias mentioned.
For mouse input, the user is going to have to click down, move it, and let go so handle the MouseDown, MouseUp, and the Scroll events of the trackbar like so:
private bool trackbarMouseDown = false;
private bool trackbarScrolling = false;
private void trackbarCurrentPosition_Scroll(object sender, EventArgs e)
{
trackbarScrolling = true;
}
private void trackbarCurrentPosition_MouseUp(object sender, MouseEventArgs e)
{
if (trackbarMouseDown == true && trackbarScrolling == true)
Playback.SetPosition(trackbarCurrentPosition.Value);
trackbarMouseDown = false;
trackbarScrolling = false;
}
private void trackbarCurrentPosition_MouseDown(object sender, MouseEventArgs e)
{
trackbarMouseDown = true;
}
I had a similar problem, only with a range TrackBar Control. Same idea applies to this also, only it's easier for this case.
I handled the MouseUp Event on the TrackBar to launch the procedures I needed, only after you would let go of the mouse button. This works if you drag the bar to your desired position or just click it.
private void rangeTrackBarControl1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
YourProcedureHere();
}
i solved the problem for my application with two events:
catch the Trackbar-ValueChange-Event
whithin the value-change event disable the valuechange event and enable the MouseUp-Event
public MainWindow()
{
//Event for new Trackbar-Value
trackbar.ValueChanged += new System.EventHandler(trackbar_ValueChanged);
}
private void trackbar_ValueChanged(object sender, EventArgs e)
{
//enable Trackbar Mouse-ButtonUp-Event
trackbar.MouseUp += ch1_slider_MouseUp;
//disable Trackbar-ValueChange-Event
trackbar.ValueChanged -= ch1_slider_ValueChanged;
}
private void trackbar_MouseUp(object sender, EventArgs e)
{
//enable Trackbar-ValueChange-Event again
trackbar.ValueChanged += new System.EventHandler(trackbar_ValueChanged);
//disable Mouse-ButtonUp-Event
trackbar.MouseUp -= trackbar_MouseUp;
//This is the final trackbar-value
textBox.AppendText(trackbar.Value);
}
ATTENTION: this works if the trackbar is moved by mose. It is also possible to move the trackbar by keyboard. Then futher code must be implemented to handle this event.

MouseWheel Event Fire

I have a problem on calling my private method on MouseWheel event. In fact my mouse wheel event gets fired properly when i only increment a variable or display something in Title bar etc. But when i want to call a private method, that method gets called only one time which is not the requirement i want to call that method depending on the speed of scroll i.e. when scroll is done one time slowly call the private method one time but when the scroll is done in high speed call the private method more than one time depending on the scroll speed.
For further explanation i am placing the sample code which displays the value of i in Title bar and add it in the Listbox control properly depending on the scroll speed but when i want to call the private method more than one time depending upon the scroll speed, that method gets called only one time.
public partial class Form1 : Form
{
ListBox listBox1 = new ListBox();
int i = 0;
public Form1()
{
InitializeComponent();
// Settnig ListBox control properties
this.listBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listBox1.FormattingEnabled = true;
this.listBox1.Location = new System.Drawing.Point(13, 13);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(259, 264);
this.listBox1.TabIndex = 0;
// Attaching Mouse Wheel Event
this.listBox1.MouseWheel += new MouseEventHandler(Form1_MouseWheel);
// Adding Control
this.Controls.Add(this.listBox1);
}
void Form1_MouseWheel(object sender, MouseEventArgs e)
{
i++;
this.Text = i.ToString();
this.listBox1.Items.Add(i.ToString());
// Uncomment the following line to call the private method
// this method gets called only one time irrelevant of the
// mouse wheel scroll speed.
// this.LaunchThisEvent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.listBox1.Select();
}
private void LaunchThisEvent()
{
// Display message each time
// this method gets called.
MessageBox.Show(i.ToString());
}
}
How to call the private method more than one time depending upon the speed of the mouse wheel scroll?
You can try using the MouseEventArgs.Delta field to calculate the number of calls:
int timesToCall = Math.Abs(e.Delta/120);
for (int k = 0; k < timesToCall; ++k)
{
this.LaunchThisEvent();
}
`

Multiple MouseHover events in a Control

I'm trying to implement a custom control in C# and I need to get events when the mouse is hovered. I know there is the MouseHover event but it only fires once. To get it to fire again I need to take the mouse of the control and enter it again.
Is there any way I can accomplish this?
Let's define "stops moving" as "remains within an x pixel radius for n ms".
Subscribe to the MouseMove event and use a timer (set to n ms) to set your timeout. Each time the mouse moves, check against the tolerance. If it's outside your tolerance, reset the timer and record a new origin.
Pseudocode:
Point lastPoint;
const float tolerance = 5.0;
//you might want to replace this with event subscribe/unsubscribe instead
bool listening = false;
void OnMouseOver()
{
lastpoint = Mouse.Location;
timer.Start();
listening = true; //listen to MouseMove events
}
void OnMouseLeave()
{
timer.Stop();
listening = false; //stop listening
}
void OnMouseMove()
{
if(listening)
{
if(Math.abs(Mouse.Location - lastPoint) > tolerance)
{
//mouse moved beyond tolerance - reset timer
timer.Reset();
lastPoint = Mouse.Location;
}
}
}
void timer_Tick(object sender, EventArgs e)
{
//mouse "stopped moving"
}
I realize that this is an old topic but I wanted to share a way I found to do it: on the mouse move event call ResetMouseEventArgs() on the control that catches the event.
Why not subscribe to the MouseMove events in the MouseHover event, then unsubscribe in the MouseLeave event
Edit:
One way to determine if the mouse is stopped would be to start a timer every time you get a mouse move event, when the timer has elapsed then you could consider the mouse stopped.
The reason for removing the MouseMove events on MouseLeave would allow you to only receive events while the mouse is over your control.
This was the cleanest implementation I could come up with. It works quite well:
[DllImport("user32.dll")]
static extern IntPtr SendMessage
(
IntPtr controlHandle,
uint message,
IntPtr param1,
IntPtr param2
);
const uint WM_MOUSELEAVE = 0x02A3;
Point lastMousePoint = Point.Empty;
void richTextBox_MouseMove(object sender, MouseEventArgs e)
{
if (Math.Abs(e.Location.X - lastMousePoint.X) > 1 || Math.Abs(e.Location.Y - lastMousePoint.Y) > 1)
{
lastMousePoint = e.Location;
SendMessage((sender as RichTextBox).Handle, WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);
}
}
void richTextBox_MouseHover(object sender, EventArgs e)
{
foo();
}
Here is a slightly simpler version that doesn't rely on a Timer. Just record the time of the last mouse move then to compare current time whenever you want to check if the mouse is hovering.
private DateTime mouseMoveTime = DateTime.Now;
private Point mouseMoveLoc = new Point();
private bool IsHovering()
{
return (mouseMoveTime.AddMilliseconds(SystemInformation.MouseHoverTime)).CompareTo(DateTime.Now) < 0;
}
private void myControl_MouseMove(object sender, MouseEventArgs e)
{
// update mouse position and time of last move
if (Math.Abs(e.X - mouseMoveLoc.X) > SystemInformation.MouseHoverSize.Width ||
Math.Abs(e.Y - mouseMoveLoc.Y) > SystemInformation.MouseHoverSize.Height)
{
mouseMoveLoc = new Point(e.X, e.Y);
mouseMoveTime = DateTime.Now;
}
}
Or a really short version that doesn't have a distance tolerance.
private DateTime mouseMoveTime = DateTime.Now;
private bool IsHovering()
{
return (mouseMoveTime.AddMilliseconds(SystemInformation.MouseHoverTime)).CompareTo(DateTime.Now) < 0;
}
private void myControl_MouseMove(object sender, MouseEventArgs e)
{
mouseMoveTime = DateTime.Now;
}
Just use if (IsHovering())... when you need to check if the mouse is moving.
One thing I have noticed though is that MouseMove is not fired when you are dragging. You can however copy the mouseMoveTime/Loc update code to your drag event to get round this.

Categories