Wait for Winform UserControl to load - c#

I have a custom Winform Infopath UserControl which loads a form from a sharepoint library. I have a series of function calls, after opening the form, to pull some data from the form, even a take screenshot function. But the form load takes a lot of time, and the other functions finish up too quickly, much before the form load, which gives me wrong results.
Is there any way I can have a wait function, that waits for the infopath form to finish loading before the other functions are called?(in c#)
--Update
Example Code:
Inside the UserControl, I have a form initialize function, that basically loads the form
public void InitializeInfoPathForm(string myurl)
{
if (this.IsInitialized) return;
CreateForm(new Uri(myurl),null);
}
public void CreateForm(
Uri formUrlName,
Stream dataStream)
{
TestInitialization();
try
{
this.formControl.Close();
// Open / create a form
if (dataStream != null)
formControl.Open(
formUrlName.ToString()
);
else
{
formControl.Open(
formUrlName.ToString());
}
RefreshView(UIStatesForm.DocumentReadMode);
}
catch (Exception)
{
RefreshView(UIStatesForm.NoDocumentAvailable);
throw;
}
}
The Winform looks like this:
public partial class Form1 : Form
{
public Form1(string sharepoint_url)
{
InitializeComponent();
this.infoPathUserControl1.InitializeInfoPathForm(sharepoint_url);
takescreenshot();
}
}
I tried putting the takescreenshot() in a Form1_Load and Form1_Shown eventhandlers, but still the screenshots occur much earlier than the form load because FormControl.Open() takes a lot of time. I could put the screenshot function in a button_click event, but i want to automate this procedure. I even tried putting it ina button_click procedure and called button.PerformClick from the Form_Load eventhandler.
Pls Advice.

You use the event UserControl.Activated for the purpose
UserControl1.Activated += new EventHandler(SeriesOfFunctions);
Then write your code in this SeriesOfFunctions
You have other choices like UserControl.Load,UserControl.Activating

You can define a bool and set it to false, after your infopath has been loaded, set it to true and only in that case start the intialize_component in your form constructor. or something like that!

If I am understanding your question correctly, it seems as though you are running into a race condition where you have tasks executing on separate threads.
If you can... I'd use the On_Load function of the Form; however, if you need to have separate threads for speed so that the background tasks don't have to wait for the form prior to starting, take a look at the ManualResetEvent. You could have the form.on_load event call set on the event once it is correctly loaded.

Related

Showing form from async method and waiting on a result causes an extra blank form to appear

Okay, here goes.
I am building a C# Winforms application on top of the CefSharp Winforms minimum example.
I have moved all of the code out of the main form's constructor and into a new method called Initialize(). So I instantiate and then call Initialize.
var shellForm = new ShellForm();
shellForm.Initialize();
Application.Run(shellForm);
Initialize() is declared async void, because somewhere in the middle of it a form is displayed, from which user input is required before continuing.
//set up the form based on the number of projects and their names
ProjectSelect projectSelectForm = new ProjectSelect();
projectSelectForm.SetUpForm(projPaths); //show/hide buttons based on length of list
projectSelectForm.TopMost = true;
projectSelectForm.Show();
await projectSelectForm.WhenEventTriggered();
The task-waiting stuff is implemented in this class:
public static class ProjectSelectEventWaiter
{
public static Task WhenEventTriggered(this ProjectSelect ps)
{
var tcs = new TaskCompletionSource<bool>();
ProjSelEvtHandler handler = null;
handler = (selected) =>
{
tcs.TrySetResult(true);
ps.ProjectSelected -= handler;
};
ps.ProjectSelected += handler;
return tcs.Task;
}
}
Everything works fine, except that when projectSelectForm is shown, another blank form appears above and to the left. When I click close on this form, the VS debugger stops. But if Initialize() is not declared async (and the await is removed), then this blank form doesn't appear.
blank form appearing at top left
I guess there are several no-no's in all of the above. I'm not a software engineer, I'm doing this project to demonstrate an important business case and there's no one else to do it.
The questions are: why might this form be appearing, and how could I try prevent it from appearing?
The problem is most likely that the wrong form becomes the mainForm for the application. It seems like you intend ShellForm to be the main form for the application, but you are displaying another form before this has been fully initialized. This is usually not a good idea.
You probably want to fully create your main form before displaying any other form to solve the problem. This might require rewriting to do a multi-step initialization, first load the shellform, then ask for project, and finally do the final initialization of the shellForm.
Another workaround could be to create a hidden form as the main-form. That way you could display your ProjectSelection-form before the shell-form.

c# custom messageBox show

I need to show up custom messageBox and close it when operation is done.
Problem is messageBox appear normal but without added label wich shows message , i can see only white space...After job is done , messageBox closing work normal.
here is code
public void resetirajSve() {
MyMessageBox poruka = new MyMessageBox();
poruka.Show();
analizaPodataka();
glProstor.Rows.Clear();
poruka.Close();
}
I tried using poruka.ShowDialog() but then code wont to continue executing.. with showDialog method label apper normal...
public partial class MyMessageBox : Form
{
public MyMessageBox()
{
InitializeComponent();
}
}
Some pointers:
1.It seems your custom MessageBox actually inherits a Form. The default MessageBox is rather versatile - see some examples, so there's no need to reinvent the wheel. Focus on more important things.
2.Using ShowDialog() will block all actions from executing on the Form until the newly opened form (you call it MyMessageBox, but it's actually a Form).
3.You could just add a ProgressBar control to display the task's progress and use the wait cursor until it is finished.
4.To avoid the program window becoming unresponsive until the method has finished executing (it can be an issue if it takes a long time), you might want to take a look at asynchronous programming. Here's an example.

Invoke a javascript function in WebBrowser and wait until javascript event fires

I'm working in .NET, C# to be specific, creating a Win Forms UserControl, which contains a WebBrowser control. The WebBrowser control hosts a page, which in turn uses a third-party javascript component. The problem I'm having is with invoking a javascript function to initialize the third-party javascript component and block the UI in the Windows Forms application until the component has been initialized, which the component notifies you of through an internal javascript event that it has.
Part of the problem is that the only way to change any configuration parameter of the third-party javascript component is to re-initialize it with the new configuration. So for example, if you want to make it read-only you have to re-initialize it with the read-only parameter.
I've got everything working in terms of being able to call the Document.InvokeScript and then in the web page call the UserControl method using window.external but the problem I'm having is how to block the UserControl code that makes the call to initialize the javascript component so that it waits and doesn't return control to the user until the initialization of the javascript component has been completed.
The reason I need it to work this way is because if I have a "Read-Only" checkbox on the form that changes the the ReadOnly property of the UserControl to control whether the javascript component shows the data as read-only and the user clicks that checkbox really quickly you will either get a javascript error or the checkbox will get out of sync with the actual read-only state of the javascript component. This seems to happen because the control hasn't re-initialized yet after it's configuration has changed and you're already trying to change it again.
I've spent hours and hours trying work out a way to make it work using everything from AutoResetEvent to Application.DoEvents and so on, but don't seem to be able to get it working.
The closest I've found is Invoke a script in WebBrowser, and wait for it to finish running (synchronized) but that uses features introduced in VS2012 (and I'm using VS2010) and I don't think it would work anyway as it's a bit different in that you're not waiting for a javascript event to fire.
Any help would be greatly appreciated.
The problem in the first place is the requirement to "block" the UI thread until some event has been fired. It's usually possible to re-factor the application to use asynchronous event handlers (with or without async/await), to yield execution control back to the message loop and avoid any blocking.
Now let's say, for some reason you cannot re-factor your code. In this case, you'd need a secondary modal message loop. You'd also need to disable the main UI while you're waiting for the event, to avoid nasty re-entrancy scenarios. The waiting itself should to be user-friendly (e.g., use the wait cursor or progress animation) and non-busy (avoid burning CPU cycles on a tight loop with DoEvents).
One way to do this is to use a modal dialog with a user-friendly message, which gets automatically dismissed when the desired JavaScript event/callback has occured. Here's a complete example:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WbTest
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IScripting))]
public partial class MainForm : Form, IScripting
{
WebBrowser _webBrowser;
Action _onScriptInitialized;
public MainForm()
{
InitializeComponent();
_webBrowser = new WebBrowser();
_webBrowser.Dock = DockStyle.Fill;
_webBrowser.ObjectForScripting = this;
this.Controls.Add(_webBrowser);
this.Shown += MainForm_Shown;
}
void MainForm_Shown(object sender, EventArgs e)
{
var dialog = new Form
{
Width = 100,
Height = 50,
StartPosition = FormStartPosition.CenterParent,
ShowIcon = false,
ShowInTaskbar = false,
ControlBox = false,
FormBorderStyle = FormBorderStyle.FixedSingle
};
dialog.Controls.Add(new Label { Text = "Please wait..." });
dialog.Load += (_, __) => _webBrowser.DocumentText =
"<script>setTimeout(function() { window.external.OnScriptInitialized}, 2000)</script>";
var canClose = false;
dialog.FormClosing += (_, args) =>
args.Cancel = !canClose;
_onScriptInitialized = () => { canClose = true; dialog.Close(); };
Application.UseWaitCursor = true;
try
{
dialog.ShowDialog();
}
finally
{
Application.UseWaitCursor = false;
}
MessageBox.Show("Initialized!");
}
// IScripting
public void OnScriptInitialized()
{
_onScriptInitialized();
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IScripting
{
void OnScriptInitialized();
}
}
Which looks like this:
Another option (a less user-friendly one) is to use something like WaitOneAndPump from here. You'd still need to take care about disabling the main UI and showing some kind of waiting feedback to the user.
Updated to address the comment. Is your WebBrowser actually a part of the UI and visible to the user? Should the user be able to interact with it? If so, you cannot use a secondary thread to execute JavaScript. You need to do it on the main thread and keep pumping messages, but WaitOne doesn't pump most of Windows messages (it only pumps a small fraction of them, related to COM). You might be able to use WaitOneAndPump which I mentioned above. You'd still need to disable the UI while waiting, to avoid re-entrancy.
Anyhow, that'd still be a kludge. You really shouldn't be blocking the execution just to keep the linear code flow. If you can't use async/await, you can always implement a simple state machine class and use callbacks to continue from where it was left. That's how it used to be before async/await.

Get Data from Windows Form into C# program

I want my C# program to collect data. Then, when the OK button is clicked, I want this data to be loaded into my program -- such as into variables in Main(), or into data members of a class I have constructed, and I want the form to then go away -- not be hidden, but actually closed. Alas, read as I might in the book I have, the data collected by the form stays in the form, execution proceeds within the form, and the same form is used to display the result of the program's computations. Sheesh. All I want is to capture the form's information, close the form and proceed with my program. I would appreciate some tips on geting data from a form into Main() or into a class's data members.
Thanks,
Lucky
What you want to do is perfectly acceptable, it just isn't typical.
When you use Visual Studio to generate a WinForms project, it creates one form for you and
generates a call to Application.Run(new Form1()). For this version of the Run() method, your application will exit when the "main form" (the one passed to Run(), in this case Form1) closes.
There are three overloads (versions) of Application.Run(). For your purposes, you need to use a different overload:
Application.Run(ApplicationContext)
When you use this overload of Run(), you get to control when the application exits. In a nutshell, here's one way you could do it:
Create a class which inherits
ApplicationContext.
In its constructor:
Create your form.
Subscribe to its Closing and Closed events.
Show your form.
In your FormClosing event handler,
get the data from the form.
In your FormClosed event handler, do
whatever you want to do with the
data, and then exit the thread (or do something else).
Here's a crude example, but I will leave out the code for the form itself. Assume the form simply has one TextBox which has its Modifiers property set to Public. (This is NOT an elegant way to get data from a form, but that part is up to you).
namespace Me.MyDemo
{
static class Program
{
[STAThread]
static void Main()
{
MyApplicationContext ac = new MyApplicationContext();
Application.Run(ac);
}
class MyApplicationContext : ApplicationContext
{
string _text = "";
public MyApplicationContext()
{
Form1 f1 = new Form1();
f1.FormClosing += new FormClosingEventHandler(f1_FormClosing);
f1.FormClosed += new FormClosedEventHandler(f1_FormClosed);
Console.WriteLine("I am here. Showing form in 1 second...");
Thread.Sleep(1000);
f1.Show();
}
void f1_FormClosing(object sender, FormClosingEventArgs e)
{
_text = (sender as Form1).textBox1.Text;
}
void f1_FormClosed(object sender, FormClosedEventArgs e)
{
Console.WriteLine("You wrote: " + _text);
Console.WriteLine("I will go away in 2 seconds...");
Thread.Sleep(2000);
ExitThread();
}
}
}
}
Of course, you don't have to exit the thread. You can leave it running if there are other things for your program to do. It will just run as a windowless process. Just remember that you're responsible for eventually ending it.
For more help, look at the documentation for the System.Windows.Forms.Application class, and the ApplicationContext class.
For getting the data from your form, there are many ways to approach this. The simple way is to just give your form some public properties. A more sophisticated way would be to create a data class and use data-bound controls on your form.
You're writing in WinForms? As far as I know a Windows application has to have a window, even if it's one pixel by one pixel.
Have you seen any other Windows applications that work the way that you want yours to work? Opens a window, the window closes, but the program keeps on running? This is generally considered undesired behavior, similar to viruses and trojans.
You can create a console application or a Windows service with no GUI, of course.
What is the application doing behind the scenes after the data is entered? If it's just doing some calculations and saving to disk, uploading, or printing, leave the window open for that and then exit when it's done. Possibly include a progress bar.

Removing a Control from a Form

So I've got some serious problems with removing a Control from a Form of my application. It's kinda messed up but I can't change anything. I have a form and I have a separated user Control. The control opens an exe file and shows a progress bar while loading it's bytes. And here comes the problem. I do all of it with a BackgroundWorker and when the worker_DoWorkerCompleted method is called the original form should show a MessageBox and remove the Control.
BackGround_Loader bgLoad = new BackGround_Loader();
bgLoad.Location = new Point(this.Width/2 - bgLoad.Width/2, this.Height/2 - bgLoad.Height/2);
this.Controls.Add(bgLoad);
bgLoad.BringToFront();
bgLoad.AddReferences(this.executableFile, this.SourceReader);
bgLoad.occuredEvent();
At first I set the control's location to be in the middle of the Form itself. Then I add the control to the form, and bring it to the front. After these I send the path of the executable and a RichTextBox's reference to this. With the occuredEvent I start the BackgroundWorker itself. And here comes my problem. I should show a MessageBox in the Form when the in the bgLoad the backgroundworker gets to the DoWorkerCompleted status. Kindly I have no idea how to do it. It works just perfect however the control stays in the middle of the form.
UI actions must be performed on the main UI thread. The events that get raised from the background worker thread are (obviously) in a different thread.
You need something like the following code:
private void backgroundWorker_DoWork(object sender, AlbumInfoEventArgs e)
{
// Check with an element on the form whether this is a cross thread call
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { AddToGrid(e.AlbumInfo); });
}
else
{
AddToGrid(e.AlbumInfo);
}
}
In this case AddToGrid is my method for adding a row to a DataGridView, but in your case it will be a method that does what you need to do.
Similarly for the backgroundWorker_RunWorkerCompleted method
See this MSDN example
I could find a way to solve the problem but I don't really like it. In the addReferences method I pass the Form itself and an object of the bgLoad class. Then in the RunWorkerCompleted I check if the control is on the form and if it is then I remove it.
bgLoad.AddReferences(this, bgLoad, this.executableFile, this.SourceReader);
...
private void worker_DoWorkerCompleted(object sender, DoWorkerEventArgs e) {
if(this.MainForm.Controls.Contains(this.Control) {
this.MainForm.Controls.Remove(this.Control);
}
}
Like this it works but it's awful for me.

Categories