I have a big project (30ish developed classes) and I want to make a loading bar for it. I have a current progress and total progress bars. I also have to roughly describe what is happening during the current process.
The way that I did it is manually (haters gonna hate) go through the most greedy process and did estimations of how long it is gonna take (when it is a for loop, just do step ups each iteration).
I read that Background_Worker would be much more smoother, but as I read on, it appeared to me that I still do have to go to every chunk of code and say that I want to increment the progress bar (The idea that I have to do it do describe the process made sense to me).
So my question was: is there a way to sort of encapsulate your method in a "block", which would automatically allocate the progress bar times? Or is there a more efficient way to do what I'm doing?
The best way would be to put your main processing in a different thread to NOT overload your GUI so it doesn't freeze up (user frustration) and then do your progress bar in the GUI thread (or make another thread for the bar, too). All depends on the preference.
But NEVER!! Freeze up the UI. The users just think it is dead and try to force close it.
is there a way to sort of encapsulate your method in a "block", which
would automatically allocate the progress bar times?
No, progress bar is for showing a progress, so you have to set the start, end and current state values form outside (from the caller), because only caller knows what is it going to execute and have, potentially, control over it.
Yes, it is possible, if you visualize infinit bar, that does not provide to a user some quantitative information about action executing, but informs to him, that something is going on. You also can provide some text description, that changes based on current action, so user can see that "something is going on there".
There is nothing worse from UX point of view, then leaving user uniformed, confused in front of the UI that is doing something, which is not clear.
So the both approaches are good, imo.
Perhaps a background worker with the progress bar. Use a global variable to store the progress.
public class SomeClass
{
public int Progress { set; get; }
public void Method1()
{
...
Progress++;
}
public void Method2()
{
...
Progress++;
}
...
}
As each method is called, increment the progress and call the background worker to report progress.
I know there are better solutions to this but this is just an idea.
Related
sorry if this is a silly question, I am new to C#, so please give me a break.
I am working on Revit API. currently, Revit API doesn't support multi-threading operations.
my question is how to stop a loop without calling a new thread?
I am trying to get a snapshot and I am waiting for the user to pick a snap, so I put an infinite loop till the condition meets
while (!Clipboard.ContainsImage()) //loop till user get a clipboard image
{
}
but what if I want to abort this???
I have tried
private void Abort_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
abort = true;
}
}
but this method requires threading ....any suggestions!!
You want to look into asynchronous processing patterns in Revit - check for instance The Building Coders description of IExternalEvent.
It might take a little while to wrap your head around it, but let me try to explain:
Split your code up into two parts: The first part runs up until the moment you ask the user to pick a snap (I'm assuming this happens in another application?). In a separate thread, poll the clipboard (or register your application as a clipboard viewer) in your while loop. Don't forget to Sleep() while you're polling! As soon as your polling thread finds a picture, create an instance of an IExternalEvent subclass and post it to Revit. When Revit has some spare time, it will execute the Execute method, passing in a UIApplication that you can use to do the rest of your code.
Revit doesn't let you access it's API from multiple threads at the same time, but during the Execute method of an IExternalEvent subclass, you are guaranteed to be in the correct thread for running code.
You can also look into the OnIdling event - basically, this gets called each time Revit has some spare time too. So instead of using a while loop in a separate thread, you could just place your clipboard checking code in the event handler for the OnIdling event and create a mechanism (a flag) to tell you wether you are currently waiting for a snap or not...
I started programming just few month ago so I am a pure beginner
but I needed a math app with Heavy probability calculation and an UI to view/interact so I searched around and found C#.net was the best way... fantastic power...
It will help me a lot to progress if I am able do this :
Wpf window with textbox, in textbox a number is printed each time the calculation is done but the Ui should respond everytime
I tried to do it using latest net 4.5 because the app need to be fast (so use the most recent tech and not timer or background task)
and it work but the Ui is stuck and cannot move (because of my bad access to the text box I think)
If guys could help me it will be great and I thank you all because I learned a lot with your posts!
Here is my wrong newbie code
private Random rng;
public MainWindow()
{
rng = new Random();
InitializeComponent();
Task.Run((Func<Task>) Calc);
}
private async Task Calc()
{
while (true)
{
textBox1.Dispatcher.Invoke
(DispatcherPriority.Normal
, new Action(delegate()
{
textBox1.Text = rng.NextDouble().ToString();
}
)
);
}
}
You're tightlooping, admittedly in a non-UI-thread, but adding a bazillion delegates to invoke in the UI thread... so your UI is just too busy.
All your work is basically going on in the UI thread at the moment - you're not even computing rng.NextDouble() in the background thread.
Also note that you've got an async method without an await expression, which should have triggered a compiler warning - you should take heed of that.
You say you don't want to use a "background task" - but that's exactly what you are doing by calling Task.Run.
Now I'm assuming your real code doesn't actually just need to create random numbers. What does your real calculation code look like, and which thread does it occur in?
I have a winform application that can get pretty unresponsive during heavy calculations. For example, when user presses F10 key, the program will starts some heavy stuff and remains unrsponsive for a while (I know this is not a desired way of program flow but I don't want to alter the way program works at the moment).
Now the problem is, during this time if user presses F10 again, the program will start doing the same thing as soon as it has done the first process.
How to disable capturing keys at a certain point and enable it again?
The program is not "capturing the key", it is queued by the operating system because your main UI-Thread is busy with your calculations and doesn't handle anything at that time. The only thing i could think of what you could do is to check that a certain time has elapsed after the last run has finished before you allow another run. An ugly hack in my humble opinion.
But, yeah, thats why you should use backgroundworkers or threading. Using a BackgroundWorker is a lot easier than it may seem at the beginning.
Ideally you should use a BackgroundWorker here but as you said
I don't want to alter the way program works at the moment).
So I won't go into that path.
What you can do is when you detect F-10 for the first time set a bool value to true and next time whenever you detect f-10, check if the bool is already true or not. If it is already true don't start the heavy operation again simply skip the code.
At the end of heavy processing set the bool to false again.
I would agree with Jason on the whole - hacks and temporary fixes have a nasty habit of becoming 'features' of a program.
However, to answer your question, I would suggest having a disable flag in your program that disables the desired functionality whilst your calculations are running. You could then put in the event handler a check for the flag :
public bool DisableFlag { get; set; }
public void MyKeyEventHandler(object sender, EventArgs e)
{
if (DisableFlag)
{
return;
}
// Do stuff
}
Hope that helps!
Cheers,
Chris.
EDIT :
Thinking about Ken's comment, and this is true, the event will be queued and it will only be useful as long as some events are bleeding through. So, the other option is to disable the even handler altogether by doing
myControl =- MyKeyEventHandler;
and then
myControl =+ MyKeyEventHandler;
when the calculations are finished. This way, no events are queued and you avoid the problem as described by Ken!!
I've got a "Loading Progress" WPF form which I instantiate when I start loading some results into my Main Window, and every time a result is loaded I want the progress bar to fill up by x amount (Based on the amount of results I'm loading).
However what happens is that the Progress bar in the window stays blank the entire time, until the results have finished loading, then it will just display the full progress bar.
Does this need threading to work properly? Or is it just to do with the way I'm trying to get it to work?
//code snippet
LoadingProgress lp = new LoadingProgress(feedCount);
lp.Show();
foreach (FeedConfigGroup feed in _Feeds) {
feed.insertFeeds(lp);
}
//part of insertFeeds(LoadingProgress lbBox)
foreach (Feeds fd in _FeedSource) {
lpBox.setText(fd.getName);
XmlDocument feedResults = new XmlDocument();
feedResults.PreserveWhitespace = false;
try {
feedResults.Load(wc.OpenRead(fd.getURL));
} catch (WebException) {
lpBox.addError(fd.getName);
}
foreach (XmlNode item in feedResults.SelectNodes("/rss/channel/item")) {
//code for processing the nodes...
}
lpBox.progressIncrease();
}
If more code is needed let me know.
Consider accessing the progressbar from the external thread thru a delegate asynchronously using the ProgressBar's Dispatcher.Invoke.
This post might be helpful.
I suggest you to use another thread to do the processing. The progress bar is filled at the end, so you didn't make a mistake on its programming.
In general, you should avoid to do processing in the user interface thread.
So, how many feeds and how many items? I'll wait for you to count them in order to set the total length of the progress bar.
...
What was that? You wouldn't know the total until after most of the work is already done? Well, sounds like the progress bar isn't the correct control to use.
You're doing (at least what should be) asynchronous work here. Definitely do it in a worker thread. Definitely let the user know work is going on in the background. But don't use a progress bar. You can't know exactly what point in the process you are at and how much longer you have to go until all the long-lasting processes have completed. Most of your users will see that your progress bar as the lie it is and will laugh at your suck.
Just use the standard "work is going on in the background" fadey-circle-thingy on your RSS feed box (an Adorner would work nicely) and be done with it.
Using this video I managed to get it working, Dispatch.BeginInvoke (Except for one which needed to be Dispatcher.Invoke) was the way to go. If anyone wants the final code let me know and I can post it up.
My application is multithreaded it has f.ex 25 active threads, and each thread pushes it status to list view item by delegate.
example:
private delegate void SetBackColorDelegate(int index, Color color);
private void SetBackColor(int index, Color color)
{
if (listView1.InvokeRequired)
{
listView1.Invoke(new SetBackColorDelegate(SetBackColor), new object[] { index, color });
}
else
{
listView1.Items[index].BackColor = color;
}
}
Depending on status it changes item color and etc. And it twinkles a lot, it looks very nasty :)
Maybe you can suggest how to avoid this ? How to speedup drawing ? Or maybe I should consider of start using some different component ?
While I wait for your response to my comment. If the flickering is a double buffering issue then the accepted answer from this question will get you on your way. Set your listview to use the style from the answer and you're good to go. There is a performance penalty for this since it will ensure that the colour updates only happen in sync with the monitor's refresh rate (normally around 60 times per second), but it will stop the flicking/tearing that occurs when the updates fall between monitor refreshes.
Consider making each thread push its state to some shared data structure, and then poll that once per second from the UI thread using a timer. That way:
You don't have to worry about invoking from the non-UI threads
You won't get a "busy" UI
You'll still be reasonably up-to-date (vs an approach of saying "don't update if I've already updated recently)
I don't know much about UI timers in terms of efficiency - it's possible that such a timer would be relatively costly, so you may need to have some cross-thread communication anyway: make each non-UI thread enable the timer if it's not already enabled, and disable it when you do the update. That way you'll always be a second behind reality, but still with only one status update per second.