I am trying to preload server form in the constructor of client form, in a separate thread. My reason is that server load is time consuming.
Here's the client form, you can see in the constructor that I am calling Preload(). There's also a button, clicking on it should show the server, which should be fast since the server form is already preloaded:
public partial class Form1 : Form
{
ServerUser server = null;
public Form1()
{
InitializeComponent();
Preload();
}
public async void Preload()
{
await Task.Run(() =>
{
server = new ServerUser();
server.LoadDocument();
server.ShowDialog();
}
);
}
private void button1_Click(object sender, EventArgs e)
{
server.Show();
}
}
Here I try to preload form ServerUser in constructor of Form1 and if I click on button1 Server form show faster
And here's the server form:
public partial class ServerUser : Form
{
public ServerUser()
{
InitializeComponent();
}
public void LoadDocument()
{
ConfigureSource();
}
public void ConfigureSource()
{
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
UpdateControls();
}
private void UpdateControls()
{
richTextBox1.Rtf = Resource1.ReferatPPC_Bun___Copy;
}
}
You need to rethink your design. You should create all forms from the main UI thread, and offload the heavy lifting(non UI stuff) to the background threads. Calling UI methods from background threads results in undefined behavior or exceptions.
Also, I think you misunderstand what await does. You call Preload() synchronously even though it is an asynchronous method. This means that by the time server.Show(); is called, Server might still be running one of these methods:
server = new ServerUser(); //you should move this outside of Task.Run()
server.LoadDocument(); //implement this method using background threads
server.ShowDialog(); //this will actually throw an exception if called via Task.Run();
From your sample I suppose LoadDocument is the expensive operation. You should rewrite that method to run on a background thread and make ServerUser show a loading screen untill LoadDocument() completes. Make sure that all UI methods from LoadDocument are called via BeginInvoke
or proper async/await.
Send in constructor;
public partial class ServerUser : Form
{
public ServerUser(Form1 form1)
{
InitializeComponent();
form1.Preload();
}
public void LoadDocument()
{
ConfigureSource();
}
public void ConfigureSource()
{
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
UpdateControls();
}
private void UpdateControls()
{
richTextBox1.Rtf = Resource1.ReferatPPC_Bun___Copy;
}
}
public partial class Form1 : Form
{
ServerUser server = null;
public Form1()
{
InitializeComponent();
Preload();
}
public async void Preload()
{
await Task.Run(() =>
{
server = new ServerUser();
server.LoadDocument();
server.ShowDialog();
}
);
}
private void button1_Click(object sender, EventArgs e)
{
server=new ServerUser(this);// or whatever you want
server.Show();
}
}
Related
I have an application where I would like to execute certain orders on a certain thread when that thread is idling. So I created a manager to handle this for me, launched of a form.show and created a thread manager:
public class ThreadManager
{
static List<ThreadAble> orders = new List<ThreadAble>();
public static bool running = false;
public static void execute(ThreadAble action)
{
orders.Add(action);
}
public static void RegisterAPIThreadAndHold()
{
running = true;
Application.Idle += Application_Idle;
}
private static void Application_Idle(object sender, EventArgs e)
{
if (orders.Count != 0)
{
ThreadAble f = orders.First();
orders.Remove(f);
f.execute();
}
}
}
public interface ThreadAble {
void execute();
}
public static class formstuff{
private static void ShowDialogThreaded(){
form.Show(owner);
ThreadManager.RegisterAPIThreadAndHold();
}
}
}
I then try to use this using it by:
public class TestRegister : ThreadAble
{
public void execute()
{
throw new NotImplementedException();
}
}
ThreadManager.execute(new TestRegister());
Now this should throw an exception, however it doesn't. I have also tried with more complicated behaviour and breakpoints but this code seems to never get executed. Am I misunderstanding how the Application_Idle works? Is there another way to make it so that this thread starts executing my code (has to be this thread) when it's done with handling the GUI code and not doing anything else (it might be required to do other things a well).
I already veritfied that RegisterAPIThreadAndHold() is executed.
I have an winform and an interface callback that continuously sends updates. I want to be able to update a label1.Text from the callback interface. However since the intrface runs on an seperate thread I do not think i can do it directly so I was trying to use a delegate and invoke.
However I am running into an error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created - at
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
Here is the full code.
public partial class Form1 : Form
{
MyCallBack callback;
public delegate void UpdateDelegate(string myString);
public UpdateDelegate myDelegate;
public Form1()
{
InitializeComponent();
myDelegate = new UpdateDelegate(UpdateDelegateMethod);
callback = new MyCallBack(this);
CallBackInterfaceClass.SetCallBack(callback);
callback.OnUpdate();
}
public void UpdateDelegateMethod (String str)
{
label1.Text = str;
}
}
class MyTestCallBack : Callback
{
public Form1 form1;
public SomeObject so;
public MyTestCallBack(Form1 form)
{
this.form1 = form;
}
public void OnUpdate(SomeObject someobj)
{
so = someobj;
OnUpdate();
}
public void OnUpdate()
{
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
}
}
Two questions.
Can anyone explain what I am doing wrong?
Is this actually best method to do this?
Here is the answer based on the reply by bokibeg (see below) with a couple of modifications to make it work:
public partial class Form1 : Form {
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action(() =>
{
callback_MyTestCallBackEvent(sender, e);
}));
return;
}
label1.Text = e.SomeObject.GetDisplayString();
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(this, e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
} }
Here's what I'd do:
public partial class Form1 : Form
{
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
// TODO: Invoke code here with e.g. label1.Text = e.SomeObj.ToString()...
}
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
public MyTestCallBack()
{
//
}
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
}
}
It separates the GUI logic from whatever that thread is doing. It fires an event and it's Form's duty to do whatever it pleases with it.
I'm not sure if this compiles, I wrote it in text pad. Tell me if you have questions.
You probably just learned about delegates and got carried away with it, this is similar as it uses an event but the event is properly placed in the "back end" logic - form may or may not use it. Also notice how form's code is a lot better, it doesn't have so much boilerplate code just to implement some background service class.
Note however that MyTestCallBackEvent event may keep firing even after you close the form so make sure you unsubscribe from it when the form closes or disposes (or whenever you feel like form doesn't need it anymore).
Oh and I almost forgot: the original error you were getting is because you called Invoke when one wasn't required and Form definitely wasn't ready for it. Read this question to see how to safely invoke controls.
Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}
I have been searching for an answer to my particular problem for a while with no success.
I have a task in my program that takes a few seconds and I want to show a new form while that task is being done. The new form has a loadingbar and some text.
I need to show the new form parallel to the task otherwise the task will not start untill I close the new form.
This is the solution I have now:
private void loadingBar()
{
frmLoading frm = new frmLoading("Please wait while the database is being backed up", "This might take several days.");
frm.ShowDialog();
}
public void Backup()
{
Thread load = new Thread(new ThreadStart(loadingBar));
load.Start();
///Execute a task.
load.Abort();
}
So, this works OK but my question is: Wouldn't it be better to close the the form "frm" in the load-thread to make it stop?
You could do this a few ways...
1 - You could do as BendEg suggested and invoke you frmClose once you are ready
Something like;
Invoke(new Action(Close));
or
Invoke(new Action(() => frmMain.Close()));
2 - Or you could simply use a background worker;
The simplest way to demonstrate this would be to add a BackgroundWorker to your form, and use the events provided;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
MessageBox.Show(#"Please wait while the database is being backed up", #"This might take several days.");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Running"); //Execute a task
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Ended"); //Dispose of any objects you'd like (close yor form etc.)
}
I hope this helps.
You can declare the form on Class-Level and later close it with an invoke.
MSDN-Windows Forms Invoke
Like this:
public class Class1
{
private Form myForm;
public Class1()
{
myForm = new Form();
}
public void DoSomeWork()
{
// ===================================================
// Do Some Work...
// ===================================================
myForm.Invoke(new MethodInvoker(this.Hide));
}
public void Hide()
{
myForm.Hide();
}
public void Backup()
{
myForm.ShowDialog();
Thread load = new Thread(new ThreadStart(DoSomeWork));
load.Start();
}
}
I think this can work for you.
void YourMethod()
{
WaitForm wf = new WaitForm();
Invoke(new PleaseWaitDelegate(Launch),wf);
bool val = BoolMethodDoWork();
Invoke(new PleaseWaitDelegate(Close), wf);
if(val)
{
MessageBox.Show("Success!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return;
}
MessageBox.Show("Damn!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
delegate void PleaseWaitDelegate(Form form);
void Launch(Form form)
{
new System.Threading.Thread(()=> form. ShowDialog()).Start();
}
void Close(Form form)
{
form.Close();
}
I think this will help you (if i understood you right):
Parallel.Invoke(() => somemethod(), () =>
{
someothertaskmethod();
});
I placed methods as example to demonstrate 2 tasks running.
You nee to use the proper using statement using System.Threading.Tasks;
I hope that the title and this simple example says everything.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateLabel(string str)
{
label1.Text = str;
MessageBox.Show("Hello");
}
private void buttonIn_Click(object sender, EventArgs e)
{
UpdateLabel("inside");
}
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod();
}
}
public class MyClass
{
public void MyMethod()
{
Form1 MyForm1 = new Form1();
MyForm1.UpdateLabel("outside");
}
}
When I'm trying to change lable1 from MyClass it does nothing. But I can get to the UpdateLable method from outside, it says Hello to me, it just doesn't change the label.
Use a delegate for setting your label
public class MyClass {
Action<String> labelSetter;
public MyClass(Action<String> labelSetter) {
this.labelSetter = labelSetter;
}
public void MyMethod() {
labelSetter("outside");
}
}
.
public void buttonOut_Click(object sender, EventArgs e) {
var outside = new MyClass(UpdateLabel);
outside.MyMethod();
}
a bit unsure because the example actually leaves some bits unclear... but here is a try:
public class MyClass
{
public void MyMethod(Form1 F)
{
F.UpdateLabel("outside");
}
}
this works as long as MyClass is NOT running on a different thread - otherwise the call to UpdataLabel must be synchronized with the UI thread...
EDIT:
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod(this);
}
Either go with Yahia's way (it has been updated and will work correctly) or try the following (probably overkill for what you're trying to do... whatever that is).
UPDATE:
Based on your comment in the question, you are also doing the work in MyClass on a different thread. Code change is below.
public partial class Form1 : Form
{
// keep a reference to a MyClass object for your Form's lifetime
private MyClass _myClass;
public Form1()
{
InitializeComponent();
// Intstantiate your MyClass object so you can use it.
_myClass = new MyClass();
// Register to the MyClass event called UpdateLabel.
// Anytime MyClass raises the event, your form will respond
// by running the UpdateLabelFromMyClass method.
_myClass.UpdateLabel += UpdateLabelFromMyClass;
}
private void button1_Click(object sender, EventArgs e)
{
// Call MyMethod in your MyClass object. It will raise
// the UpdateLabel event.
// update, you are calling this on a background thread?
_myClass.MyMethod();
}
void UpdateLabelFromMyClass(string message)
{
// Update your label with whatever message is passed in
// from the MyClass.UpdateLabel event.
// UPDATE: If called from a background thread you'll need this:
this.BeginInvoke( (Action) (()=>
{
label1.Text = message;
}));
}
}
public class MyClass
{
// An event that can be raised, allowing other classes to
// subscribe to it and do what they like with the message.
public event Action<string> UpdateLabel;
public void MyMethod()
{
// Raise the UpdateLabel event, passing "Outside" as
// the message.
UpdateLabel("Outside");
}
}
After wasting a ton of time on what should be a simple task and trying every answer on stack overflow, I said, if C# wants to make it stupid hard to change the text of a simple label, I am going to come up with a stupid fix.
Here is what you do:
In Form1 or whatever form has the label you want add:
public void setStatus()
{
lblStatus.Text = status;
}
public static string status;
Now, add a timer to Form1 and have it run "setStatus();" on every tick
Now, in any class, just write:
Form1.status = "Change label to this text";
you need to make both the method MyMethod and the label in question static. But if you do then you cannot access MyMethod through a new instance of the form instead you have to access it directly like Form1.MyMethod(). But if you do make the label the static visual studio will make it non-static one you access the label from the designer so you will have to keep making it static from form1.designer.cs. Also if you do make the label static change every line that refers to any of its properties so if it says this.label1.Text change it to label1.Text. This should give you the desired effect