I am trying to learn .NET programming. As a part of my learning, I tried to make some effects on buttons. It is working... but not as smooth as I imagined! Is there any better way to do this? Thank you in advance!
My need:
There are 3 buttons.
When you hover the mouse over one of them, it expands and when you mouse out from that button, it returns to its initial size.
private void button1_MouseHover(object sender, EventArgs e)
{
button1.BackColor = Color.White;
button1.Width = 130;
button1.BringToFront();
}
private void button1_MouseLeave(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
button1.Width = 75;
}
private void button2_MouseHover(object sender, EventArgs e)
{
button2.BackColor = Color.Gray;
button2.Width = 130;
button2.BringToFront();
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
button2.BackColor = Color.Red;
button2.Width = 75;
}
private void button3_MouseHover(object sender, EventArgs e)
{
button3.BackColor = Color.DimGray;
button3.Width = 130;
button3.BringToFront();
}
private void button3_MouseLeave(object sender, EventArgs e)
{
button3.BackColor = Color.Red;
button3.Width = 75;
}
So first off, you don't want to do the exact same thing 3 times. Create a single method to add the appropriate handlers for a button, and then just write the code once to handle any given button.
Note that you can go into the expand/contract tick handlers and use the percentComplete value to set the height as well, to move the color along a spectrum (this would involve some mathematics of colors to do though) or to alter any other aspect of the button. If you're really motivated to generalize it you could add a parameter to the method of Action<double> that does something to the object based on the given percent progress.
public void AddAnimation(Button button)
{
var expandTimer = new System.Windows.Forms.Timer();
var contractTimer = new System.Windows.Forms.Timer();
expandTimer.Interval = 10;//can adjust to determine the refresh rate
contractTimer.Interval = 10;
DateTime animationStarted = DateTime.Now;
//TODO update as appropriate or make it a parameter
TimeSpan animationDuration = TimeSpan.FromMilliseconds(250);
int initialWidth = 75;
int endWidth = 130;
button.MouseHover += (_, args) =>
{
contractTimer.Stop();
expandTimer.Start();
animationStarted = DateTime.Now;
button.BackColor = Color.DimGray;
};
button.MouseLeave += (_, args) =>
{
expandTimer.Stop();
contractTimer.Start();
animationStarted = DateTime.Now;
button.BackColor = Color.Red;
};
expandTimer.Tick += (_, args) =>
{
double percentComplete = (DateTime.Now - animationStarted).Ticks
/ (double)animationDuration.Ticks;
if (percentComplete >= 1)
{
expandTimer.Stop();
}
else
{
button.Width = (int)(initialWidth +
(endWidth - initialWidth) * percentComplete);
}
};
contractTimer.Tick += (_, args) =>
{
double percentComplete = (DateTime.Now - animationStarted).Ticks
/ (double)animationDuration.Ticks;
if (percentComplete >= 1)
{
contractTimer.Stop();
}
else
{
button.Width = (int)(endWidth -
(endWidth - initialWidth) * percentComplete);
}
};
}
If you are using WinForms, animations are going to be rather painful and you will have to handle them yourself via Timer objects.
If you are getting into .NET and want to make cool-looking applications with animatons and styling, I highly recommend you look at WPF instead. It can do animations very easily though C# or XAML.
While it is still possible in WinForms, it will take far more development time where as those features are built into WPF already (and optimized).
When you modify a controls properties, it takes effect instantaneously. What you desire is something that is usually known as some type of fade or tweening. There might be libraries out there to do this, but if you wanted to write this yourself for fun, you can use a Timer object, and on each tick update the color.
What you would do is set a color as the TargetColor somewhere(this is a variable or property you make up), and then start a timer that ticks maybe every 10 milliseconds. In each tick, you look at the start time, and how long has passed since then. If you want the animation to take place of a full second, then that is 1000 milliseconds. So during each tick, you look at the amount of time that has passed, maybe 200 milliseconds, then divide 200/1000 to get the fraction of time that you have gone into the animation. Then you look at a difference between the Start and Target Color, multiply that difference by the fraction, and add the result to the start color. In other words, 200 milliseconds into an animation that last 1000 milliseconds, means you are 20% into the animation. Thus you want to set the color to be whatever color is 20% from the start color towards the end color.
There's alot you could do to refine this. Perhaps having a subclass Button control that encapsulates the timer and exposes functions/properties to track start/end color, animation transition time, etc. Most animated UI features like this let you specify how long the animation should last, and then it interpolates the inbetween states as it transitions. This is the origin of the term tweening, as it comes from transitioning from one state to another by inbetweening
Related
I am developing software that adds if a button is clicked 5 times, a variable is incremented by '1'
IF A then B++
everything is good, but now I want the system to reset its counter if that 5 times did not happen within 10 seconds. I.e the speed of clicking matters.
If I click too slow, the increment should not happen even though I clicked 5 times as it exceeds that 10 secs period.
Any suggestion?
This could be done much nicer but it should work:
DateTime time = new DateTime();//time of first click
int counter = 0;
void button_click(object sender, EventArgs e)
{
if(counter == 0)
{time = DateTime.Now}
else if(counter == 5)
{
if( DateTime.Now.Subtract(time).Duration().Seconds <= 10)
{/*Do some cool stuff*/}
else
{counter = -1;}
}
counter++;
}
I'd do something like this:
const int ClicksRequired = 5;
readonly TimeSpan ClickTimeSpan = new TimeSpan(0, 0, 10);
Queue<DateTime> _clicks = new Queue<DateTime>();
private void clickTarget_MouseUp(object sender, MouseEventArgs e)
{
var currentTime = DateTime.Now;
_clicks.Enqueue(currentTime);
if (_clicks.Count == ClicksRequired)
{
var firstTime = _clicks.Dequeue();
if (currentTime - firstTime <= ClickTimeSpan)
{
MessageBox.Show("Hello World!");
_clicks.Clear();
}
}
}
I use Queue to keep track of clicks because you don't know which mouse click will actually be the first click until you have five clicks in the time window. You need to know the time of the fifth click back in time and that click changes with each subsequent click.
I use MouseUp instead of Click because Click might not fire the correct number of times if clicks occur within the system double-click interval (because those get routed to DoubleClick).
I want to make hovering button in my game. Because when my cursor touch the button it will go to another screen immediately. I don't like this so much. I use xna 4.0 with visual studio 2010 to make this project. (use kinect without wpf)
How to use timer in this case ? Please help me
if (Hand.contian(Button) && holdtime == targetHoldtime)
{
}
You have to manage time by yourself based in elapsed time per frame:
ft = GameTime.Elapsed.TotalSeconds; // Xna
ft= 1/30f; // 30fps
And can be done in similar way to this:
class Button {
public float Duration = 1; // One second
public Rectangle Bounds; // Button boundaries
public float Progress { get{ return Elapsed/Duration; } }
float Elapsed = 0;
public void Update(float ft) {
if (Bounds.Contains( HandPosition ))
{
if (Elapsed<Duration) {
Elapsed += ft;
if (Elapsed>Duration) {
Elapsed = Duration;
OnClick();
}
}
} else {
Elapsed = 0;
}
}
}
I would first suggest that you look through the SDK documentation and the built in KinectInteraction controls. They may provide you with what you are looking for. Most notably SDK 1.7 removed that "HoverDwell" button in favor of a "press" action, which is a more natural interaction in a gesture system. You may want to look at using that motion instead.
If you truly desire a "click on hover" type action, you can look at the code in SDK 1.6 for an example. Several examples are available online at the Kinect for Windows CodePlex repository. The specific control example you are looking for is in the "BasicInteraction-WPF" project, and is called HoverDwellButton.
The "button" is actually a ContentControl which means you can place any content in there to make it a button. It can be a simple image, or a complex Grid. It has all the hooks to fire events when the timer on your hover goes off.
There is a decent amount of complexity in this control, which is what makes it work for a wide range of applications. At the core of the interaction is a simple DispatcherTimer.
private void OnPreviewHandEnter(object sender, HandInputEventArgs args)
{
if (this.trackedHandHovers.FirstOrDefault(t => t.Hand.Equals(args.Hand)) == null)
{
// additional logic removed for answer sanity
var timer = new HandHoverTimer(DispatcherPriority.Normal, this.Dispatcher);
timer.Hand = args.Hand;
timer.Interval = TimeSpan.FromMilliseconds(Settings.Default.SelectionTime);
timer.Tick += (o, s) => { this.InvokeHoverClick(args.Hand); };
this.trackedHandHovers.Add(timer);
timer.Start();
}
args.Handled = true;
}
Notice that the Tick event is calling InvokeHoverClick, which (in part) reads as follows:
public void InvokeHoverClick(HandPosition hand)
{
// additional logic removed for answer sanity
var t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(0.6);
t.Tick += (o, s) =>
{
t.Stop();
var clickArgs = new HandInputEventArgs(HoverClickEvent, this, hand);
this.RaiseEvent(clickArgs);
this.IsSelected = false;
};
t.Start();
}
This now fires an event after a set amount of time. This event can be capture and acted upon to your liking.
Again, I first recommend looking at the newer interactions in SDK 1.7. If you still want a timed hover click action, check out the links above. I used the HoverDwellButton to great effect in several different areas.
While searching for code to fade a winform, I came across this page on the MSDN forum.
for (double i = 0; i < 1; i+=0.01)
{
this.Opacity = i;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
The for loop has a non-integer increment and, from a previous question I asked, that's not a good programming technique (due to inexact representation of most decimals).
I came up with this alternative.
for (double i = 0; i < 100; ++i)
{
this.Opacity = i/100;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
Which of these is more efficient?
If there's a better algorithm for fading a form, I'll be very glad if it is included.
Thanks.
Forget timers (pun intended).
With Visual Studio 4.5 or higher, you can just await a task that is delayed. An advantage of this method is that it's asynchronous, unlike a thread Sleep or DoEvents loop, which blocks the application during the fade (and the other aforementioned DoEvents problems).
private async void FadeIn(Form o, int interval = 80)
{
//Object is not fully invisible. Fade it in
while (o.Opacity < 1.0)
{
await Task.Delay(interval);
o.Opacity += 0.05;
}
o.Opacity = 1; //make fully visible
}
private async void FadeOut(Form o, int interval = 80)
{
//Object is fully visible. Fade it out
while (o.Opacity > 0.0)
{
await Task.Delay(interval);
o.Opacity -= 0.05;
}
o.Opacity = 0; //make fully invisible
}
Usage:
private void button1_Click(object sender, EventArgs e)
{
FadeOut(this, 100);
}
You should check if the object is disposed before you apply any transparency to it. I used a form as the object, but you can pass any object that supports transparency as long as it's cast properly.
So, first off, application.DoEvents should be avoided unless you really know what you're doing and are sure that this is both an appropriate use of it, and that you are using it correctly. I'm fairly certain that neither is the case here.
Next, how are you controlling the speed of the fading? You're basically just letting the computer fade as quickly as it can and relying on the the inherent overhead of the operations (and background processes) to make it take longer. That's really not very good design. You're better off specifying how long the fade should take from the start so that it will be consistent between machines. You can use a Timer to execute code at the appropriate set intervals and ensure that the UI thread is not blocked for the duration of the fade (without using DoEvents).
Just modify the duration below to change how long the fade takes, and modify the steps to determine how "choppy" it is. I have it set to 100 because that's effectively what your code was doing before. In reality, you probably don't need that many and you can just lower to just before it starts getting choppy. (The lower the steps the better it will perform.)
Additionally, you shouldn't be so worried about performance for something like this. The fade is something that is going to need to be measured on the scale of about a second or not much less (for a human to be able to perceive it) and for any computer these days it can do so, so much more than this in a second it's not even funny. This will consume virtually no CPU in terms of computation over the course of a second, so trying to optimize it is most certainly micro-optimizing.
private void button1_Click(object sender, EventArgs e)
{
int duration = 1000;//in milliseconds
int steps = 100;
Timer timer = new Timer();
timer.Interval = duration / steps;
int currentStep = 0;
timer.Tick += (arg1, arg2) =>
{
Opacity = ((double)currentStep) / steps;
currentStep++;
if (currentStep >= steps)
{
timer.Stop();
timer.Dispose();
}
};
timer.Start();
}
I wrote a class specifically for fading forms in and out. It even supports ShowDialog and DialogResults.
I've expanded on it as I've needed new features, and am open to suggestions. You can take a look here:
https://gist.github.com/nathan-fiscaletti/3c0514862fe88b5664b10444e1098778
Example Usage
private void Form1_Shown(object sender, EventArgs e)
{
Fader.FadeIn(this, Fader.FadeSpeed.Slower);
}
for (double i = 0; i < 1; i+=0.01)
{
this.Opacity = i;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
is more efficient as the number of floating point divisions are more machine-expensive than compared to floating point additions(which do not affect vm-flags). That said, you could reduce the number of iterations by 1/2(that is change step to i+=0.02). 1% opacity reduction is NOT noticeable by the human brain and will be less expensive too, speeding it up almost 100% more.
EDIT:
for(int i = 0; i < 50; i++){
this.Opacity = i * 0.02;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
I applied the approach of Victor Stoddard to a splashScreen. I used it in the Form_Load event to fadeIn and FormClosing event to fadeOut.
NOTE: I had to set the form's opacity to 0 before call the fadeIn method.
Here you can see the order of events rised by a winform (lifecycle):
https://msdn.microsoft.com/en-us/library/86faxx0d(v=vs.110).aspx
private void Splash_Load(object sender, EventArgs e)
{
this.Opacity = 0.0;
FadeIn(this, 70);
}
private void Splash_FormClosing(object sender, FormClosingEventArgs e)
{
FadeOut(this, 30);
}
private async void FadeIn(Form o, int interval = 80)
{
//Object is not fully invisible. Fade it in
while (o.Opacity < 1.0)
{
await Task.Delay(interval);
o.Opacity += 0.05;
}
o.Opacity = 1; //make fully visible
}
private async void FadeOut(Form o, int interval = 80)
{
//Object is fully visible. Fade it out
while (o.Opacity > 0.0)
{
await Task.Delay(interval);
o.Opacity -= 0.05;
}
o.Opacity = 0; //make fully invisible
}
In the past, I've used AnimateWindow to fade in/out a generated form that blanks over my entire application in SystemColor.WindowColor.
This neat little trick gives the effect of hiding/swapping/showing screens in a wizard like interface. I've not done this sort of thing for a while, but I used P/Invoke in VB and ran the API in a thread of its own.
I know your question is in C#, but it's roughly the same. Here's some lovely VB I've dug out and haven't looked at since 2006! Obviously it would be easy to adapt this to fade your own form in and out.
<DllImport("user32.dll")> _
Public Shared Function AnimateWindow(ByVal hwnd As IntPtr, ByVal dwTime As Integer, ByVal dwFlags As AnimateStyles) As Boolean
End Function
Public Enum AnimateStyles As Integer
Slide = 262144
Activate = 131072
Blend = 524288
Hide = 65536
Center = 16
HOR_Positive = 1
HOR_Negative = 2
VER_Positive = 4
VER_Negative = 8
End Enum
Private m_CoverUp As Form
Private Sub StartFade()
m_CoverUp = New Form()
With m_CoverUp
.Location = Me.PointToScreen(Me.pnlMain.Location)
.Size = Me.pnlMain.Size
.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
.BackColor = Drawing.SystemColors.Control
.Visible = False
.ShowInTaskbar = False
.StartPosition = System.Windows.Forms.FormStartPosition.Manual
End With
AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend) 'Blocks
Invoke(New MethodInvoker(AddressOf ShowPage))
End Sub
Private Sub EndFade()
AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend Or AnimateStyles.Hide)
m_CoverUp.Close()
m_CoverUp = Nothing
End Sub
Victor Stoddard is close, but I found that the fade was more pleasing if it started the opacity increase fast and slowed as it approached full opacity of 1. Here's a slight modification to his code:
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class EaseInForm : Form
{
public EaseInForm()
{
InitializeComponent();
this.Opacity = 0;
}
private async void Form_Load(object sender, EventArgs e)
{
while (this.Opacity < 1.0)
{
var percent = (int)(this.Opacity * 100);
await Task.Delay(percent);
this.Opacity += 0.04;
}
}
}
I am trying to make a programmable alarm clock. It should output a melody (.wav) at different moments of time. I used a usercontrol to make a digital clock. I have a timer for showing a progressbar to every second and a button that starts the process. I know exactly the times I want so I don't need any other button. I made some functions where I complete the times I need.
public void suna3()
{
userControl11.Ora = 01;
userControl11.Min = 37;
userControl11.Sec = 50;
}
and on the button click I called them. But when I start the program it is taking only the last time I made (the last function). How can I make it take all the functions?
private void button3_Click(object sender, EventArgs e)
{
userControl11.Activ = true;
suna1();
userControl11.Activ = true;
suna2();
}
I think the problem is that you are using the same timer for three different events, changing the settings of it three times. This results in only the final settings being used - the older settings are not remembered - they are overwritten.
The solution is to create a new timer for each event, each timer having its own separate settings.
You need a usercontrol for each alarm time, or change your user control so that it takes in an array of times to trigger the alarm at.
For multiple usercontrol instances, your code could look something like this:
public void suna1()
{
userControl11.Ora = 02;
userControl11.Min = 27;
userControl11.Sec = 20;
}
//...
public void suna3()
{
userControl13.Ora = 01;
userControl13.Min = 37;
userControl13.Sec = 50;
}
The alternative is to change your usercontrol so that it accepts a list of times, or has an AddAlarm() method. Something like this:
public void suna1()
{
userControl11.Alarms.Add(new Alarm() { Ora = 02, Min = 27, Sec = 20 };
//or "userControl1.AddAlarm(2, 27, 20);" if you go the method route
}
//...
public void suna3()
{
userControl11.Alarms.Add(new Alarm() { Ora = 01, Min = 37, Sec = 50 };
}
I did quite a bit of searching around and didn't find anything of much help.
Is it possible to "slide" or "move" using C#, an object from one Location to another using a simple For loop?
Thank you
I would suggest you rather use a Timer. There are other options, but this will be the simplist if you want to avoid threading issues etc.
Using a straight for loop will require that you pump the message queue using Application.DoEvents() to ensure that windows has the opportunity to actually render the updated control otherwise the for loop would run to completion without updating the UI and the control will appear to jump from the source location to the target location.
Here is a QAD sample for animating a button in the Y direction when clicked. This code assumes you put a timer control on the form called animationTimer.
private void button1_Click(object sender, EventArgs e)
{
if (!animationTimer.Enabled)
{
animationTimer.Interval = 10;
animationTimer.Start();
}
}
private int _animateDirection = 1;
private void animationTimer_Tick(object sender, EventArgs e)
{
button1.Location = new Point(button1.Location.X, button1.Location.Y + _animateDirection);
if (button1.Location.Y == 0 || button1.Location.Y == 100)
{
animationTimer.Stop();
_animateDirection *= -1; // reverse the direction
}
}
Assuming that the object you're talking about is some kind of Control you could just change the Location property of it.
So something like this:
for(int i = 0; i < 100; i++)
{
ctrl.Location.X += i;
}
Should work I think.