I have a Windows Form with a status bar which shows the current state of application.
I have a class named AppState with update the Label in the status bar and in dispose it changes the state back to "Ready".
In code when I do an operation like:
using (AppState state = new AppState("Processing..."))
{
//Do some work that take some seconds
}
But the label remains the same. I am not getting any exceptions. The label text is updated but on UI it keeps on showing previous value. Am I missing anything here?
santosc you are right, thats the only thing I am doing. Here is the AppState code
public class AppState : IDisposable
{
static string Default = "Ready";
public AppState(string status)
{
Form.StatusLabel.Text = status;
}
public void Dispose()
{
Form.StatusLabel.Text = Default;
}
}
It's always the same thing...
If you want to start something that takes a while, don't do it within your GUI thread or your GUI will freeze (no updates of label, no resizing, no moving, no whatever).
Filling your code on thousand places with Application.DoEvents() is also a bad practice.
If you have some long running task (long means > 1 sec) you should probably use a BackgroundWorker. Maybe it's a little bit harder at the beginning, but you will love it if your program gets more complex. Due to the fact, that this has already being discussed several time, here is a link with some sample code.
Now that you know the right tool (BackgroundWorker) to solve your problem, you should get it to work (or ask another question about your new specific problem).
Looks like you want to put Application.DoEvents() after setting the StatusLabel text field value. This tells Windows Forms to process the Windows event queue for your form, causing changes to be repainted.
in order to be "thread safe" use Invoke, and test with the InvokeRequired in the form like:
// code outside the myForm:-----------------------
if (myForm.InvokeRequired)
myForm.Invoke(new ChangeLabelEventHandler(ChangeLabel), "teeeest");
else
myForm.ChangeLabel("teeeest");
// code in the myForm:-----------------------------
public delegate void ChangeLabelEventHandler(string newText);
private void ChangeLabel(string newLabelText)
{
this.label1.Text = newLabelText;
}
I'm new to C# stuff, but why can't you just do something like:
private void updateStatusBar(string status)
{
if (StatusLabel.InvokeRequired)
{
StatusLabel.Invoke((MethodInvoker)(() =>
{
StatusLabel.Text = status;
}));
}
else
{
StatusLabel.Text = status;
}
}
When you want to update the status?
Maybe multiple threads could solve your problem.
The easiest way is using a BackgroundWorker.
The reason is that the UI is only able to redraw when the UI thread has nothing else to do. And you are blocking it with your calculation.
use Label.Refresh(); it saves a lot of time.This should work for u
Related
I'm trying to write a drawing library. In this drawing library there is an update function that should update every frame. I do this by using a do while loop See code below:
private void UpdateCanvas()
{
do
{
Canvas.PumpEvents();
if(UserUpdateVoid != null) UserUpdateVoid();
} while (Canvas.Exists);
}
I also have a function in which the user can set their own update function. This function is part of the SharpDraw class, see code below:
public void SetCustomUpdateFunction(Action function)
{
Console.WriteLine("updated the user function");
UserUpdateVoid = function;
Console.WriteLine(UserUpdateVoid);
}
all this is called in the following way:
public class SharpCanvas
{
private Sdl2Window Canvas;
private GraphicsDevice GraphicsManager;
private Action UserUpdateVoid = null;
public SharpCanvas()
{
WindowCreateInfo WindowInfo = new WindowCreateInfo(
200,
200,
100,
100,
WindowState.Normal,
"SharpWindow"
);
CreateCanvas(WindowInfo);
UpdateCanvas();
}
}
And the SharpDraw instance is made in the following way:
namespace test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
SharpCanvas Canvas = new SharpCanvas(200,200);
Canvas.SetCustomUpdateFunction(Update);
}
private static void Update(){
Console.WriteLine("update");
}
}
}
But the problem is that the Console.Writelines in the SetCustomUpdateFunction() are never executed. I guess this has to do with the fact that the while loop keeps the program from further execution. So my question is how do i keep the while loop running while still being able to execute different pieces of code? In unity they are able to do it :P
If there is something unclear let me know so i can clarify!
That is entirely normal. It does not mater if you are running a console application, a Windows Form or WPF/UWP application*: Only one piece of code can be executing. While one piece of code does not return, not other code can run.
You need to add some form of Multitasking into the mix. Now that looks extremely like a Console Application and those are the last place I would advise learning Multithreading in. My personal advise is to start under Windows Forms using the BackgroundWorker. It is dated and rarely used in practice, but it can help you get up to speed with the rules and conventions. But this is one area where you can ask 10 people and get 11 Opinions.
*Web Applciations are semi special. As they are pleasingly parallel and it helps with isolation usually each request is given their own Thread. But at least for each singular request, it still holds true.
When you call UpdateCanvas, you enter a loop and code never goes further. To prevent this, you should use threads, async-await or something similar else (see this answer for async-await).
You have to use Multithreading programming. Look for it on google, there are plenty of examples.
I have a piece of code that does some calculations and then calls the form.show command. Now I have a library (the revit api) that does not allow me to store variables in a project without being in the main thread.
The logical solution for this is to get the spawned thread to call the main thread using say a producer/consumer pattern with code looking a bit like this:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
However when I do this the gui does not load fully (black background, no text in buttons ext.).
I have also tried doing this using the evoke method
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
However this just tells me that it's not the main thread that's executing the commit function.
Is there another way to do this or am I just making an error.
Just to be clear I have a form.show(owner) command that throws an error if it's not executed by the main thread. I also have a commit() function that must be excused by the main thread or it throws an error. The execution must wait until a button press. But the main thread polling the gui thread for changing causes the program to hang. According to my google search it' s also possible to do something involving an external event to get back into the right context but the example given was using python to invoke c# code, is there a good way to raise an external event to get back into a given thread in c#?
Edit: based on some suggestions I have created the following code:
public class ThreadManager
{
static List<ThreadAble> orders = new List<ThreadAble>();
public static bool running = false;
public static void execute(ThreadAble action)
{
orders.Add(action);
}
static System.Timers.Timer timer;
public static void RegisterAPIThreadAndHold(ExternalCommandData commandData)
{
UIApplication uiapp = commandData.Application;
uiapp.Idling += Application_Idle;
}
private static void Application_Idle(Object o,IdlingEventArgs e)
{
if (orders.Count != 0)
{
ThreadAble f = orders.First();
orders.Remove(f);
f.execute();
}
}
}
public interface ThreadAble {
void execute();
}
However this does not appear to actually run when I use it as
public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
where ThrowError.execute() is
Throw new Exception(" this is actually being executed" );
Your first example could work if you will replace Thread.Sleep by the System.Windows.Forms.Application.DoEvents(). It should give time to paint GUI and do not froze application completly.
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
{
System.Windows.Forms.Application.DoEvents();
// Thread.sleep(100);
}
}
But this is not perfect solution to achieve this.
Better would be calling Dispatcher.Invoke command inside your dialog to perform MainThread operations.
You can use i.e. GalaSoft library - please refer to DispatcherHelper object documentation and samples.
The two ways to do this I'm aware of are with the External Event or the Idling event.
With the idling event, you'll register it, and while it is registered, your code (in the main thread) will get a callback from Revit every time that it's not busy with something else. Often a few times per second.
Once you are in the Idling callback, then you're able to create transactions and interact with the model. So your callback checks the state of the form and decides whether there is something to do.
The External Event works similarly in terms of registration, but you're able to request a trigger of the callback.
Jeremy Tammik must have 20 posts on thebuildingcoder.typepad.com on Modeless dialog / Revit stuff.
For a simple solution to this, please refer to the Revit SDK ModelessDialog ModelessForm_ExternalEvent sample application. It demonstrates exactly what you are asking for.
We want to display a "Loading, please wait..." screen before the application starts executing other functions on a Win CE 5.0 powered device. The application is being developed using .NET Compact Framework 3.5 (C#).
The issue is that, the UI is only updated once the set of current processes are completed, to a stage where other functions will only run with user interaction. C# has a Form.Shown() event, which would allow the application to run other functions, only ONCE the form has been displayed. However, the Compact-Framework does NOT include.
The solution I've approached was using multi-threading, where I would have one thread which would display the "Loading, please wait..." screen, and the other would take care of other function.
The issue with Threading is, when managing UI components, it gives an error if not applied the correct techniques:
public Form1()
{
InitializeComponent();
ThreadStart tstart = new ThreadStart(showLoad);
Thread t1 = new Thread(tstart);
Thread t2 = new Thread(checkConnect);
t1.Start();
t2.Start();
}
private void checkConnect()
{
conn.portConnect(); //Connects to port Successfully
if (conn.isConnected == true) //Variable which checks the connectivity status
{
panelMENUshow();
}
else
{
panelCONFIGshow();
}
}
private void showLoad()
{
if (imgLoading.InvokeRequired)
{
imgLoading.Invoke((Action)(() => imgLoading.Show())); //Image box displaying "Loading, please wait..." shows successfully if I use Control.Invoke
}
//If I don't use Control.Invoke I get the following error:
//"Control.Invoke must be used to interact with controls created on a separate thread."
}
On the showLoad() function, it is not a problem to add Control.Invoke. However, the functions panelMENUshow() and panelCONFIGshow() contains many bits of code that manages UI components, and it would be not practical to use Control.Invoke in every line of code referring to a UI component.
Is there a way of:
stopping the threading but carry on running the code that involves UI management?
practically manage UI components within a thread?
PS: The approach was based on the answer of this post.
Use the refresh function to force the control to be immediately redrawn.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.refresh(v=vs.90).aspx
When writing WinForm apps you want to keep the complexities of threading away from the main UI thread. I've seen too many applications with thread code weaving in and out of them, tying the logic into a knot.
Instead, model anything that requires any kind of threading as a component that, while it may use threads internally, presents a friendly, even-based model to the outside world. These components should not have any direct interaction with your UI.
Given that, your code would look something more like this (just an example, not syntax checked and probably with lots of typos etc, but it will give you an idea):
public class CommunicationObject
{
// you will probably have several EventArgs to define to pass extra info
public event EventHandler<EventArgs> Connected;
// you need this instance to dispatch events to the UI thread
private Control _invoker;
public CommunicationObject(Control invoker)
{
_invoker = invoker;
// start a thread here, or better yet, add an Enabled property or
// Start method to kick it off
}
// from the thread that is doing the real work, call this when you are connected
private void OnConnected()
{
_invoker.Invoke(() =>
{
EventHandler<EventArgs> handler = Connected;
if (handler != null)
{
handler(this, EventArgs.Empty); // eventually you might need your own event args
}
});
}
}
public class Form1 : Form
{
private CommunicationObject _comm;
public Form1()
{
InitializeComponent();
imgLoading.Show(); // show msg until connected
_comm = new CommunicationObject(this);
_comm.Connected += Comm_Connected; // wire up event handler
}
private void Comm_Connected(object src, EventArgs e)
{
if (imgLoading.Visible)
{
imgLoading.Hide(); // hide once connected
}
panelMENUshow();
}
}
Note that the form has no threads in it at all. Push that complexity into stand-alone objects. It might seem like a bit more work, but you will thank yourself later!
Using Visual Studio 2012 ulti, C# .NET WPF.
Using Tasks in my code on winforms used to be simple.
All I would do is create a delegate, create a function for my code, create a task and the event would be a simple button. Easy stuff. Problem I have is the following...
Create a thread as per-usual But the event will be on text changed.
The problem im having is thinking about the logic, if I simply change the event I cant see this working as the user could type faster than the code could run ( in this case an sql query select statement). There for it would try to run many tasks which I don't even think would work.
Basically User enters text box that used for searching an account by name or number.
In this textbox I would like to thread the entire process.
The only solution I can think of is as the text changes if there is a thread still running stop that thread and create the new one, but not sure if thats a clean way of doing it as its a sql stored procedure ill be calling.
So any body got a solution to this?
If you need any more info just ask. Ill also provide some code that currently works to give you an understanding if needed...
Set Invoke method up:
private void SetDataGrid(bool AutoGenerateColumns, Object DataSource, String DataMember, DataGridViewAutoSizeColumnsMode Mode)
{
if (this.ParetoGrid.InvokeRequired)
{
this.ParetoGrid.Invoke(new Action<bool, Object, String, DataGridViewAutoSizeColumnsMode>(SetDataGrid),
AutoGenerateColumns, DataSource, DataMember, Mode);
}
else
{
this.ParetoGrid.AutoGenerateColumns = AutoGenerateColumns;
this.ParetoGrid.DataSource = DataSource;
this.ParetoGrid.DataMember = DataMember;
ParetoGrid.AutoResizeColumns(Mode);
}
}
Call invoke method in another method:
Private void GetSomething()
{
//sql code get data
SetDataGrid(true, dataSet1, "Pareto", DataGridViewAutoSizeColumnsMode.AllCells);
}
Then simply start task on event:
private void myButton_Click(Object sender, EventArgs e)
{
Task t = new Task(() => getSomething());
t.Start();
}
As you can see simple stuff, but simple changing event seems to mess the whole logic up.
I'd recomend moving this logic from the task into a Timer callback, then have your OnTextChanged handler actually reset the timer each time it's fired (only have the timer fire once of course). By making the timer elapse after .5-1 sec, or something like that, you'll wait until all of their text has been entered before actually calling your logic. But the user's experience will still be quite responsive.
Example:
private System.Threading.Timer keyEntryTimer = new Timer(Logic,null,-1,-1);
public void HandleEvent(objet sender, EventArgs args)
{
keyEntryTimer.Change(500,-1);
}
public void Logic(objet state)
{
//Your task logic would go here to read from the text etc...
//You'll have to handle any UI updates either by firing off a task once the DB results return or using a dispatcher
}
You can simply cache all accounts when they enter this search mode. Then you can search through cached accounts when they enter text into the textbox. Doing a query in a different thread for every keypress is very heavy on the app.
I would probably do a combination of the other suggested answers and try to delay the firing of the SQL statement using a timer or some similar method, but if the user delayed long enough for the SQL to fire then try to just filter the returned results from that point forward (only if the search string is becoming more restrictive obviously). This could save you expensive SQL round trips and give you a working cache after the first hit (which should be smaller than if you tried to cache everything).
Hope this helps.
I'm trying to do something that I thought would be easy but can't figure out how to write to a label inside my stating function.
public static void StartProcessing(object data)
{
lblError.Text = "Blah Blah"
}
I get the error "An object reference is required for the non-static field, method, or property..."
So I tried creating a new instance of the label and adding it to a new instance of a control (Panel) but the lblError isn't getting displayed
public static void StartProcessing(object data)
{
Panel Panel1 = new Panel();
Label lblError= new Label();
Panel1.Controls.Add(lblError);
lblError.Visible = true;
lblError.Text = "Blah Blah";
}
there must be an easy way to do this? Which i've overlooked..
The function is getting called as follows: If I change the above to not be static I get an error message on the second line below saying the same "An object reference is required for the non-static field, method, or property..." When this function isn't static?
public object LaunchNewProcess(object data)
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(ProcessStatuses.StartProcessing),
new object[] {newProcess, allProcesses}
);
Pass the label to your static function when you call it:
public static void StartProcessing(object data, Label lblError)
{
lblError.Text = "Blah Blah"
}
Static functions don't have access to controls because the controls belong to the instance of the page (class).
Static means that all instances of a class share the same function or variable. So, an instance of a class has access to a static variable or function. However, since a static is not "aware" of any instances of the class, it cannot access the members of an instance. In fact, a static method or variable does not even require any instance of the class to exist, so how could it?
Removing the static key word from your function will also work, as others have mentioned, but I'm assuming you made it static for a reason.
EDIT
Alright, this is more complex.
So, you have some class that launches a bunch of threads, and you want it to display to the user if something went wrong? Well, your current approach is flawed because you cannot access controls of a page without the instance of the page. Also, I am not sure how this approach would fit within the page lifecycle.
Your best approach (sorry for lack of code, it's going to depend a lot on your implementation) could be something like this:
//do this before you start spawning threads
List<bool> successes = new List<bool>();
ThreadPool.QueueUserWorkItem(
new WaitCallback(ProcessStatuses.StartProcessing),
new object[] {newProcess, allProcesses, successes}
);
//you MUST wait for all your threads to complete before proceeding!
if(successes.Any(s => !s))
{
//update your error label
}
public static void StartProcessing(object data, Label lblError)
{
var dataArray = (object[3]) data;
//if there is an error
dataArray[2] = false;
}
What you want to do is actually quite difficult.
You want to create a page, start an asynchronous task, send the page to the user, and then update content on the page after the asynchronous job finishes.
The problem is that by the time the asynchronous task finishes the page has already been sent, and based on the way HTTP works once you've sent your response you're done; there's not more communicating with the client for you. You need to wait for the client to send another request if you want to update them.
This means that you need to have JavaScript code that is constantly polling the server basically asking, "Are you done yet, are you done yet, are you done yet?" until eventually the server says, "Yes, here's something to display on the page".
Fortunately, you don't need to start from scratch. Here you'll find an example by Microsoft that does all of this; you can modify it to suit your needs. It's also worth mentioning that in addition to being non-trivial to program, it also consumes a lot of resources to constantly poll the server, so be sure you really need to do this.
Option 2 is to just not start the other tasks in new threads, and execute the code serially before the page is ever returned to the user. They'll be staring at a blank screen for a while, but it'll be MUCH easier to program. The one downsize to keep an eye on here is on timeouts if the task is REALLY long running.