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;
}
}
}
Related
After being out of scripting for ages I have decided to learn a programming language and I have gone for C#. I'm getting along pretty well but now for the first time I seem to have been faced with a problem that I have not been able to solve with google.
I am making a simulated aircraft system as a learning exercise and I want to invoke a loop when an option is selected from a drop down combobox.
I have a combobox/list with three options which simulates the starter switch, the values are (0)Off, (1)On, (2)Ignition Only . In the real aeroplane, when 'On' is selected the switch locks in place for 10 seconds and then releases. So what I am trying to achieve is :
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 0)
{
//Starter is off
eng2Start.Value = 0;
}
if (starterRight.SelectedIndex == 1)
{
//starter is on
//Start Timer
eng2Start.Value = 1;
if (eng2Tourqe >= 6000)
{
//open fuel valve
// set Hot Start counter to 0
}
else
{
//ensure fuel valve stays closed
// set Hot Start counter to x+1
}
// End of Timer
// set selected index back to 0
(starterRight.SelectedIndex == 0)
}
}
I have googled and googled and the more I read the more I am getting lost in this. I have found answers containing a mass of code which I am not able to fully decipher just yet.
Is it possible to do what I want to do?
Thanks in advance for your time.
You can Add Timer to your Form and Set the Interval property to 10000(10 seconds).
from code:
if (starterRight.SelectedIndex == 1)
{
//starter is on
//Start Timer
timer1.Enabled=true;
}
//in timer tick Event write the following:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled=false;
//Statements to start aircraft
}
You could achieve this by setting it to true (or selected, or whatever you want) sleeping for 10 seconds, like this:
Thread.Sleep(10000) ;
and then set it back to false (or unselect, or whatever you want)
Another way to go would be to start a background thread that will sleep for ten seconds, and then call a method that will "unset" the button, this way, not blocking the GUI ...
Or depending on what you're using, I could probably come up with other options, but i'll take it you're trying to learn the basics atm ... :)
See this one msdn timer
And you can use Threed.Sleep(10000);
I think this should works. I didn't compile it, but with this you lock the swtich, do your stuff checking a timer that when arrives to 10 sec, re-enable your switch.
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 0)
{
//Starter is off
eng2Start.Value = 0;
}
if (starterRight.SelectedIndex == 1)
{
//starter is on
starterRight.enable = false;
StopWatch sw = new StopWatch();
sw.Start();
eng2Start.Value = 1;
if (eng2Tourqe >= 6000)
{
//open fuel valve
// set Hot Start counter to 0
}
else
{
//ensure fuel valve stays closed
// set Hot Start counter to x+1
}
if (sw.ElapsedMilliseconds <= 10000)
{
do
{
//Dummy Loop
}
while (sw.ElapsedMilliseconds > 10000)
sw.Stop();
}
else
{
// set selected index back to 0
sw.Stop();
starterRight.Enabled = true;
(starterRight.SelectedIndex == 0)
}
}
}
You can use a Timer and the Tick event. Disable your switch, and when the Timer tick, Enabled it.
Timer timerSwitchOn;
public SomeConstructor()
{
timerSwitchOn = new Timer(){Interval = 10*1000}; // 10 seconds
timerSwitchOn.Tick += new EventHandler(timerSwitchOn_Tick);
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 1)
{
//starter is on
starterRight.Enabled = false;
timerSwitchOn.Start();
}
}
void timerSwitchOn_Tick(object sender, EventArgs e)
{
timerSwitchOn.Stop();
starterRight.Enabled = true;
// set selected index back to 0
starterRight.SelectedIndex = 0;
}
In my initial question I said I had wanted to invoke a loop, I meant a timer but it seems that you all figured that out.
Thank you for the rapid answers, I am going to get stuck into them this weekend and see if I can solve my problem.
I'm creating a interface for a application at the moment. Background are all 1x1 pixel. In design time the interface is super slow (visual studio 2012). Moving components is super laggy. If I run the application it takes about 10 seconds for the interface to load.
Some parts of the interface can be hidden. Ones they become visible again they take time to build up / load part by part.
How can I improve this and make the interface much smoother / loading faster?
Source code:
namespace WindowsFormsApplication1
{
public partial class MapTool : Form
{
public int Pin_Berekenen = 0;
public int pin_Settings = 0;
public MapTool()
{
InitializeComponent();
BlackSetupUI();
panel_Berekenen.Visible = false;
panel_settings.Visible = false;
}
void BlackSetupUI()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Selectable, false);
pictureBox_RouteBerekenen.BackColor = Color.Transparent;
panel_Berekenen.BackColor = Color.Transparent;
pictureBox_settings.BackColor = Color.Transparent;
button_pin_berekenen.FlatAppearance.BorderSize = 0;
button_Berekenen.FlatAppearance.BorderSize = 0;
label_berekening1.Parent = pictureBox_bar;
label_berekening1.BackColor = Color.Transparent;
label_Berekening2.Parent = pictureBox_bar;
label_Berekening2.BackColor = Color.Transparent;
label_verschil.Parent = pictureBox_bar;
label_verschil.BackColor = Color.Transparent;
button_pin_settings.FlatAppearance.BorderSize = 0;
label_RouterBerekenen.Parent = pictureBox_bar;
label_Settings.Parent = pictureBox_bar;
pictureBox_settings.Parent = pictureBox_bar;
pictureBox_RouteBerekenen.Parent = pictureBox_bar;
pictureBox_innectis.Parent = pictureBox_bar;
label_innectis.Parent = pictureBox_bar;
}
void WhiteSetupUI()
{
}
private void MapTool_MouseClick(object sender, MouseEventArgs e)
{
if (Pin_Berekenen == 0)
{
panel_Berekenen.Visible = false;
}
else
{
return;
}
if (pin_Settings == 0)
{
panel_settings.Visible = false;
}
else
{
return;
}
}
private void button_pin_berekenen_Click(object sender, EventArgs e)
{
switch (Pin_Berekenen)
{
case 0:
Pin_Berekenen = 1;
button_pin_berekenen.Image = Properties.Resources.Pinned;
break;
case 1:
Pin_Berekenen = 0;
button_pin_berekenen.Image = Properties.Resources.Pin;
break;
default:
break;
}
}
private void button_pin_settings_Click(object sender, EventArgs e)
{
switch (pin_Settings)
{
case 0:
pin_Settings = 1;
button_pin_settings.Image = Properties.Resources.Pinned;
break;
case 1:
pin_Settings = 0;
button_pin_settings.Image = Properties.Resources.Pin;
break;
default:
break;
}
}
private void pictureBox_settings_MouseHover_1(object sender, EventArgs e)
{
panel_settings.Visible = true;
}
private void pictureBox_RouteBerekenen_MouseHover_1(object sender, EventArgs e)
{
panel_Berekenen.Visible = true;
}
}
}
I'm just designing the interface there is no data loaded with in this application as you can see.
It is hard to tell what exactly is your interface "doing". But here are some general tips:
You can preload something while showing splash screen (with or without progress) and then doing all time consuming work (to example, constructing all forms, parsing all configurations, caching graphics, etc). Or even put main application into dll and making exe-file small with the only splash screen.
You can optimize parent/child controls redrawing and layout operations (BeginUpdate/EndUpdate and such), so you don't waste time by calling unnecessary draw operation until it's a right time for it to happens. You can use double buffering, WS_EX_COMPOSITED.
If you have to much calculations, then perhaps something has to be virtualized (you display "fast placeholders" and after caculation replace them with real data) or cached.
You can use one of many tricks to simulate faster (as user can feel) operation: show a small part of data on screen, while prepare a much bigger portion of data in the memory (sort of virtual window), add some intelligent conclusions and caching (if user is moving left, then perhaps he want to continue that way, preload more data from left or, if user is moved right by 100 pixels, perhaps he will want to return, so don't unload that data yet).
Perhaps you have to much data what you have to profile it and optimize or even use C++ to write speed demanded parts in separate dll's.
Or maybe you should redesign your UI, split common parts into control, put nested things into user controls, etc, etc..
Hide all of them then when it is fully loaded, show it up
If you want to show animation, you can use Bunifu UI (paid)
I have a Canvas control with various elements on, in this particular function I am allowing a user to drag the end point of a line around the canvas. In the MouseMove function I call e.GetPosition().
The function is, according to the VS performance analyzer, close to 30% of total CPU for the app when constantly moving around. Its pretty slow. What can I do to increase this performance?
CurrentPoint = e.GetPosition(PointsCanvas);
I've faced the same problem while using MouseMove on windows phone 8. It seems that while dragging , events (containing the coordinates you need ) are raised at regular time interval ( depending on what you do in the implementation in your listeners, every 20 ms for example). So what I did was to populate a Queue with my coordinates and create a Thread that consume that Queue by enqueue the first element and do the logic I want. Like that the logic is not done serially because it's another thread who does the job.
I don't know if I'm enough clear so please take a look to the code below :
//Class used to store e.getPosition(UIElement).X/Y
public class mouseInformation
{
public int x { get; set; }
public int y { get; set; }
public mouseInformation(int x, int y, String functionName)
{
this.x = x;
this.y = y;
}
}
private readonly Queue<mouseInformation> queueOfEvent = new Queue<mouseInformation>();
//MouseMove listener
private void wpCanvas_MouseDragged(object sender, System.Windows.Input.MouseEventArgs e)
{
//Instead of "wpCanvas" put the name of your UIElement (here your canvas name)
mouseInformation mouseDragged = new mouseInformation((int)e.GetPosition(wpCanvas).X, (int)e.GetPosition(wpCanvas).Y);
EnqueueMouseEvent(mouseDragged);
}
//Allow you to add a MouseInformation object in your Queue
public void EnqueueMouseEvent(mouseInformation mi)
{
lock (queueOfEvent)
{
queueOfEvent.Enqueue(mi);
Monitor.PulseAll(queueOfEvent);
}
}
//Logic that your consumer thread will do
void Consume()
{
while (true)
{
mouseInformation MI;
lock (queueOfEvent)
{
while (queueOfEvent.Count == 0) Monitor.Wait(queueOfEvent);
MI = queueOfEvent.Dequeue();
}
// DO YOUR LOGIC HERE
// i.e DoSomething(MI.x, MI.y)
}
}
And don't forget to create the thread in your Main() or in MainPage_Loaded(object sender, RoutedEventArgs e) method if you are Windows phone user.
System.Threading.ThreadStart WatchQueue = new System.Threading.ThreadStart(Consume);
System.Threading.Thread RunWatchQueue = new System.Threading.Thread(WatchQueue);
RunWatchQueue.Name = "Events thread";
RunWatchQueue.Start();
To be simple less you do in your MouseMove listener, more speed it will be.
You can aswell do the logic asynchronously or even use Bresenham algorithm to simulate more events.
Hope it helps.
Are you using any effects such as dropshaddow etc?
I recently had the situation where e.GetPosition() was also using 30% of the app's cpu resources, which doesn't make any sense right?
I turns out that up the visual tree there was a control applying a dropshaddow effect and that was what was slowing everything down so much...
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
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.