c# application interface is loading slow - c#

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)

Related

Save/load value&color to txt/xml

I'm completely new to C# programming. (4 days)
I did some amateur programming on Excel VB to create an dashboard for our teams. Now I want to lift it to the next level.
My dashboard has 10 buttons changing color from red to green and back each click. Each click changes the label beneath it from Text 1 to Text 2.
Function: overview of the service area who finished work and handed to us.
I got 10 different voids for each of the areas, because I didn't manage to link the clicked button to the label, so I just used 10. "northwest", "east", "southeast" and so on.
void northwest(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Red":
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
case "LimeGreen":
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
lbl_nw.Text = "North-West";
break;
default:
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.Black;
lbl_nw.Text = "Service";
break;
}
}
Second: I got 145 buttons (the teams) changing between 5 different colors each click. We use this to clarify the state of the team. like "n/a", "available", "on the way" etc.
void MyButtonClick(object sender, EventArgs e)
{
Button button = sender as Button;
Control ctrl = ((Control)sender);
switch (ctrl.BackColor.Name)
{
case "Gainsboro": // switch from n/a to available
ctrl.BackColor = Color.LimeGreen;
ctrl.ForeColor = Color.White;
break;
case "LimeGreen": // switch from available to on the way
ctrl.BackColor = Color.Yellow;
ctrl.ForeColor = Color.Black;
break;
case "Yellow": // switch from on the way to fully occupied
ctrl.BackColor = Color.Orange;
ctrl.ForeColor = Color.Black;
break;
case "Orange": // switch from fully occupied to omitted
ctrl.BackColor = Color.Red;
ctrl.ForeColor = Color.White;
break;
case "Red": // switch from omitted to back at home
ctrl.BackColor = Color.Aqua;
ctrl.ForeColor = Color.Black;
break;
default:
ctrl.BackColor = Color.Gainsboro;
ctrl.ForeColor = Color.Black;
break;
}
}
Now the actual problem: Because my former dashboard can only be used by one person (on one central computer) I wanted to make my new dashboard multi-user friendly.
So my intention is to save the colors/label names into an txt file or xml file (forget about xml. its toooo complex for me) and load it automatically if there were any changes on the board by any user.
I searched but can't find any solution I really understand (because of missing knowledge).
I have tried the following:
void save(object sender, EventArgs e)
{
TextWriter txt = new StreamWriter("C:\\Users\\shift.txt");
txt.Write(button1.Text);
txt.Write(button1.ForeColor);
txt.Write(button1.BackColor);
txt.Close();
}
At this solution I would need to write every button I want to store. Problem: I get something like this:
North-West Color [LimeGreen] Color [Black].
I think the correct solution would be like North-West;LimeGreen;Black, but I cant figure out how! Even then I don't know how to load back all values to the correct objects.
And I tried to loop through them with:
for (int i = 1; i < 155; i++)
But I got problems with the objects (e.g. "is a type but used as an method") every combination.
Second attempt was via xml. But this is way to complex for me.
I tried it like this:
void savedata(object sender, EventArgs e)
{
DataSet d = new DataSet();
DataTable t = new DataTable();
d.Tables.Add(t);
t.Columns.Add(new DataColumn("Name",typeof(string)));
t.Columns.Add(new DataColumn("FontColor",typeof(string)));
t.Columns.Add(new DataColumn("Background",typeof(string)));
t.Rows.Add(button6.Text, button6.ForeColor, button6.BackColor);
t.Rows.Add(button7.Text, button7.ForeColor, button7.BackColor);
t.Rows.Add(button8.Text, button8.ForeColor, button8.BackColor);
t.Rows.Add(button9.Text, button9.ForeColor, button9.BackColor);
t.Rows.Add(button10.Text, button10.ForeColor, button10.BackColor);
t.Rows.Add(button11.Text, button11.ForeColor, button11.BackColor);
t.Rows.Add(button12.Text, button12.ForeColor, button12.BackColor);
t.Rows.Add(button13.Text, button13.ForeColor, button13.BackColor);
t.Rows.Add(button14.Text, button14.ForeColor, button14.BackColor);
t.Rows.Add(button15.Text, button15.ForeColor, button15.BackColor);
d.WriteXml("C:\\Users\\Shift.xml");
}
It saves, yes, but how do I load it back into the correct objects?
I don't understand anything about de/serialization, I just read some code and tried to adopt it and failed big.
My question:
Does anyone have a proper solution for the text file how to save/load/format correctly? The save should be done if there is a new change, same goes for the load. So automatically. Every user can open the dashboard, the file storage would be on our server where everyone has access to it to save/load it via dashboard.
I would really appreciate a possible solution for this. Like I said, I'm a beginner and this problem driving me nuts.
Everything works fine, except for the save/load mechanic.
Just to mention:
i cant download any other software, so i need to stick to windows programms.
for saving and loading from a flat file (.txt, .xml, .json, .whatver) I use the following solution, it's honestly very easy and takes the hard work away. You will need to download and install NewtonSoft package from nuget. But trust me it makes everything easy.
Let's create a hypothetical scenario similar to yours. You have many users and want to save information for each of them. Let's start with a user class.
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Theme { get; set; }
}
Now lets create a little 'database' or 'data holder' class that will actually store and lookup our info. It will have a FilePath variable where we will save our flat file, a List of all the Users and some functions to help get everything:
public static class DataHolder {
public static string FilePath { get; set; }
public static List<Users> Users { get; set; }
static DataHolder()
{
// Initialize the variables
FilePath = "C:/Temp/MyData.json";
Users = new List<User>();
Init();
}
static void Init() { }
public static void LoadData() { }
public static void SaveData() { }
}
Let's start by creating a file to save all the data in.
using System.IO;
using NewtonSoft;
...
static void Init() {
// Check if file exists
if (File.Exist(FilePath))
{
LoadData();
return;
}
// It doesn't so we create it
using (var sw = new StreamWriter(FilePath)){
// Create empty list of users
var emptyUserList = new List<User>();
// Convert it into Json using NewtonSoft
var fileText = JsonConvert.SerializeObject(emptyUserList);
// Write it to the file
sw.Write(fileText);
}
}
Above we used Newton to create a new file with an empty Json object in it. Let's see how loading and saving will work:
public static void LoadData() {
using (var sr = new StreamReader(FilePath)){
Users = JsonConvert.Deserialize<List<User>>(sr.ReadToEnd());
}
}
public static void SaveData() {
using (var sw = new StreamReader(FilePath)){
sw.Write(JsonConvert.Serialize(Users));
}
}
And that is the main bulk of the class. Let's implement a GetUser() and AddUser() function that searches for a user based on Id and then I'll show you an example of how to use the class:
using System.Linq;
...
public static User GetUser(int id) => Users.FirstOrDefault(x => x.Id = id);
public static void AddUser(User user) => Users.Add(user);
Example:
public static void Main(string[] args){
// Test the saving
TestSaving();
// Test the loading
TestLoading();
}
static void TestLoading() {
// Get a user
var loadedUser = DataHolder.GetUser(1);
if (loadedUser != null)
Console.WriteLine("Success");
}
static void TestSaving() {
// Create a new user and add it
var user = new User() {
Id = 1,
Name = "Fred",
Theme = "Red"
};
// Save the data in memory
DataHolder.AddUser(user);
// Save the data to file
DataHolder.SaveData();
}

C# - Enable/Disable MenuStrip Item Based On TextBox Value

I am trying to disable some menu strip items while a textbox is displaying "Calculating...". Once that value goes away, I wish to re-enable the menu items. Its purpose is not to interrupt MD5/CRC32 calculations. So far, I've tried various method of code, and have had no luck so far. What's listed below should work, but for some reason it does not. Any help would be appreciated.
// THIS PART WORKS
if (boxMD5.Text.Contains("Calculating") == true)
{
openROMToolStripMenuItem.Enabled = false;
saveROMDataToolStripMenuItem.Enabled = false;
asTXTToolStripMenuItem.Enabled = false;
asHTMLToolStripMenuItem.Enabled = false;
}
// THIS PART DOES NOT WORK
else if (boxMD5.Text.Contains("Calculating") == false)
{
openROMToolStripMenuItem.Enabled = true;
saveROMDataToolStripMenuItem.Enabled = true;
asTXTToolStripMenuItem.Enabled = true;
asHTMLToolStripMenuItem.Enabled = true;
}
I can't quite tell you why the code isn't doing what you expect, but I can make a suggestion that will change your approach and may help achieve your goal at the same time. What you are trying to do shouldn't be to disable the menu when the textbox contains "Calculating" but instead you should disable the menu while the calculations are being performed. From a user/UI perspective, these are the same thing, but the inner-workings of your program know better.
Based on you PasteBin code, try this:
private void openROMToolStripMenuItem_Click(object sender, EventArgs e)
{
//Other code omitted for brevity
if (File.Exists(OpenFileDialog1.FileName))
{
UpdateUI("Calculating...");
backgroundWorker1.RunWorkerAsync(OpenFileDialog1.FileName);
}
//Other code omitted for brevity
}
and
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateUI(e.Result.ToString());
}
where the new method UpdateUI() looks like this:
void UpdateUI(string hash)
{
var calculating = hash == "Calculating...";
if (!calculating)
{
progressBar1.Value = 0;
}
openROMToolStripMenuItem.Enabled = !calculating;
saveROMDataToolStripMenuItem.Enabled = !calculating;
asTXTToolStripMenuItem.Enabled = !calculating;
asHTMLToolStripMenuItem.Enabled = !calculating;
boxMD5.Text = hash;
}
Also, notice how you are able to just put !calculating in the if statement rather than calculating == false. This is because the value is already true or false so you don't have to compare it to anything to figure that out. The same thing applies to your original code but you don't need it anymore with this approach.

Pause the while loop until the button is pressed w/o using event handler C#

I am struggling to workout how to create something that essentially pauses my while loop until my button1 is pressed, I know about the event handler button1_Click but I don't think that will work in this situation as I have lots of loops nested in each other on my form_load.
Any help would be highly appreciated!
This is a snipped of my code where I want the loop to be 'paused' with the notes:
while (reader2.Read())
{
QuestionSpace = Convert.ToString(reader2["Question Space"]);
label1.Text = QuestionSpace;
if (button1.Click = true) // if the button is clicked)
{
// continue with the while loop (I am going to add an INSERT SQL query in here later)
}
else
{
// pause until the button is pressed
}
}
My whole code for the form:
public partial class CurrentlySetTestForm : Form
{
private int QuestionID { get; set; }
private string QuestionSpace { get; set; }
public CurrentlySetTestForm()
{
InitializeComponent();
}
private void CurrentlySetTestForm_Load(object sender, EventArgs e)
{
string y = GlobalVariableClass.Signedinteacher;
MessageBox.Show(y);
Convert.ToInt32(y);
string connectionString = ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString;
SqlConnection connect = new SqlConnection(connectionString);
connect.Open();
SqlCommand command18 = new SqlCommand("SELECT [QuestionID] FROM QuestionStudentAssociation WHERE ( [StudentID]=#Signedinstudent)", connect);
command18.Parameters.AddWithValue("#Signedinstudent", y);
var reader = command18.ExecuteReader();
while (reader.Read())
{
QuestionID = Convert.ToInt32(reader["QuestionID"]);
SqlCommand command19 = new SqlCommand(#"SELECT [Question Space] FROM Questions WHERE ( [QuestionID] = #currentQID )", connect);
command19.Parameters.AddWithValue("#currentQID", QuestionID);
try
{
var reader2 = command19.ExecuteReader();
while (reader2.Read())
{
QuestionSpace = Convert.ToString(reader2["Question Space"]);
label1.Text = QuestionSpace;
if (button1.Click = true) // if the button is clicked)
{
// continue with the while loop (I am going to add an INSERT SQL query in here later)
}
else
{
// pause until the button is pressed
}
}
}
catch (SyntaxErrorException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
MessageBox.Show("Done one loop");
}
}
}
}
Sounds like your not ready to learn TPL
So maybe a BackgroundWorker , you can paint it on the form
To make the click cancel the background worker have a look at Cancel backgroundworker
I would some time to learn TPL as its going to create a simpler and more elegant solution.
As for pausing I would refactor the code, you should not keep the reader open waiting on the user.
You do want event-driven response to UI events, always. However, I guess that you don't want to split your logic into a state machine by hand (where each event triggers progress to the next state). Well, you're in luck, the C# compiler has some keywords to build state machines automagically so you don't have to manage the details.
There are actually two different mechanisms for continuation-passing style implemented in C#. The old one, yield return, works great if your UI events are pretty much interchangeable (or you're only interested in one). Works like this:
IEnumerator<int> Coroutine;
// this could be a Form_Load, but don't you need to get the user information before making the database connection?
void BeginQuiz_Click( object sender, EventArgs unused )
{
Coroutine = RunQA();
}
IEnumerator<int> RunQA()
{
// connect to DB
// show first question on UI
return ContinueQA();
}
IEnumerator<int> ContinueQA()
{
// you can use a while loop instead if you really want
for( int question = 0; question < questionCount; ++question )
{
// check answer
if (/* too many wrong answers*/) {
// report failure in DB
yield break;
}
// get next question from DB
// show new question on the UI
// wait for UI action
yield return question;
}
// report score in DB
// update UI with completion certificate
}
void AnswerButton_Click( object sender, EventArgs unused )
{
answer = sender;
Coroutine.MoveNext(); // MAGIC HAPPENS HERE
}
void TimeoutTimer_Tick( object sender, EventArgs unused )
{
answer = TimeoutTimer;
Coroutine.MoveNext();
}
The magic comes from yield return. Every time the function reaches yield return, the compiler saves what you were doing. When the button click event comes and calls MoveNext, the compiler generates code that starts where yield return paused everything, and keeps going from there until the next yield return.
Important note, the code inside ContinueQA doesn't start when RunQA() does return ContinueQA(); It actually starts on the first MoveNext(). So split your code between RunQA() and ContinueQA accordingly.
If you need different pause reasons at different places in your code, then async/await will be more helpful.
A better way to handle this would be the use of a timer. This would allow the form to draw it's controls and handle all input, such as clicking the button.
Adjust the timer interval (ms) to your needs.
Another way of doing this would be, as Mehrzad Chehraz said, to use multi-threading.
On a side note, I would strongly recommend condition checks over the try/catch checks if possible.
Enable/Disable the timer using the button and call the loop when the timer ticks.
Example:
Timer loopTimer = new Timer();
private void Form1_Load(object sender, EventArgs e)
{
loopTimer.Interval = 100;
loopTimer.Tick += loopTimer_Tick;
loopTimer.Enabled = true;
}
void loopTimer_Tick(object sender, EventArgs e)
{
//perform the loop here at the set interval
}
private void button1_Click(object sender, EventArgs e)
{
//pause/play the loop
loopTimer.Enabled = !loopTimer.Enabled;
}

How to wait for a click in c#

I am currently taking a beginner's class in c#. We missed 2 consecutive classes because the teacher couldn't be there. So we didn't really see what we needed to do this. He said to just go see on MSDN, but that is usually way too complicated for me. So here is my problem:
I have to create a "Simon Says" program. Here is my current code (sorry for the French variables):
public partial class Form1 : Form
{
const byte LIMITE = 255;
const byte LIMITEBOUTON = 5;
byte[] _abyBouton = new byte[LIMITE];
Random _rand = new Random();
public Form1()
{
InitializeComponent();
}
//Blinks the Button. Works correctly.
void AnimerBouton(Button btnBouton, Color Cocoleur)
{
btnBouton.BackColor = Color.Black;
btnBouton.ForeColor = Color.White;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
btnBouton.BackColor = Cocoleur;
btnBouton.ForeColor = Color.Black;
Update();
System.Threading.Thread.Sleep(500); // C'est inscrit en miliseconde
}
private void btnDémarrer_Click(object sender, EventArgs e)
{
//Creates an array with the 255 eventual moves.
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
_abyBouton[byIndex] = (byte)_rand.Next(1, LIMITEBOUTON);
}
for (byte byIndex = 0; byIndex <= LIMITE - 1; byIndex++)
{
//Takes care of the current progress in the game.
for (byte byIndex2 = 0; byIndex2 <= byIndex; byIndex2++)
{
switch (_abyBouton[byIndex2])
{
case 1:
{
AnimerBouton(btn1, Color.Green);
}
break;
case 2:
{
AnimerBouton(btn2, Color.Red);
}
break;
case 3:
{
AnimerBouton(btn3, Color.Yellow);
}
break;
case 4:
{
AnimerBouton(btn4, Color.Cyan);
}
break;
}
//Wait to see if the click is correct. No idea how to do this.
}
}
}
}
So I have to wait for the user to click a button and see if it is the correct one. I looked around and it was talking about events, but I couldn't grasp what I needed to do. I would appreciate some help on how to do this.
Thanks!
In the designer, on the properties window, click the lightning bolt icon. You will get a list of events for the selected control. Make sure the btnBouton control is selected, and find the Click event in the list. You should see btnDemarrer_Click in the drop down list. Select it. Now when the button is clicked, it should call your btnDemarrer_Click handler.
When you have not already written a handler, you can double-click the event in the list, and it will generate the method skeleton for you automatically. You can also double-click the control itself to generate the default event handler for that control. (In the case of buttons, I think the default event is the click event.)
If you want a particular method to wait until some work is done, you could look into AutoResetEvent. An extremely simplified example might help you get on the right track:
using System.Threading;
public static AutoResetEvent arEvent = new AutoResetEvent(false);
static void Main()
{
DoWork();
arEvent.WaitOne(); //WaitOne() "pauses" Main and waits for some work to be done.
DoWork();
arEvent.WaitOne();
}
static void DoWork();
{
//Some work is done here.
arEvent.Set(); //This lets Main() continue where it left off.
}
Using this, you could have btnDémarrer_Click wait for the user input then continue on after the user has done his clicking.

Better algorithm to fade a winform

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;
}
}
}

Categories

Resources