|
Hi all,
I have written a custom control (derived from ComboBox) which lists a lot of users (userid's). I have a splashscreen where I create an instance of my form (I have made it a singleton class) so the loading of the items from the database occurs when I first create the instance (which I can do in my splashscreen).
This all works fine, and the combox items are filled during the splashscreen. However, the real drawing (or updating) occurs when I show the form, so the users still have to wait about 10 seconds before they can actually use the form.
Is there any way I can fill the combobox so it will be usable immediately when I show the form?
Thanks!
Best regards,
|
|
|
|
|
Hello,
If you set the Splash as TopMost, you could Show() the Forms you want to initialize before the customer first jumps to the page.
This will have the effect, that the startup will take longer, but the first jump to the form is faster.
Hope it helps!
All the best,
Martin
|
|
|
|
|
Thank you for your answer. However, I don't want to show any form.
In my startup, I want to use this code:
<br />
EASplashScreen splash = new EASplashScreen();<br />
splash.ShowDialog();<br />
<br />
Application.Run(MainForm.GetInstance());<br />
The splash screen does the complete initialization such as listing plugins, getting the first instance of the mainform etc. Then, it automatically closes when it is ready.
I then want the application to be completely read (at least the mainform).
Best regards,
|
|
|
|
|
Hi,
when adding a lot of items to a ListBox, ListView, ... Control performance gets
improved by:
either adding several items at once (AddRange)
or (much better) to temporarily disable the visual update (with Control.SuspendLayout and Control.ResumeLayout).
A good splash screen lets the initialization proceed in the background, and disappears
when initialization is done (possibly with a minimum delay enforced, so the user can
read and enjoy the splash).
BTW obviously you must obey the "no cross-thread" rule: controls should be
manipulated only by the thread that created them.
|
|
|
|
|
Indeed, that's why the splash screen is show first and uses a thread in the background to initialize all information.
It seems the control doesn't know any SuspendLayout function, but I already use the BeginUpdate and EndUpdate functions:
<br />
public void ListData()<br />
{<br />
if ((_connectionString == "") || DesignMode)<br />
{<br />
return;<br />
}<br />
<br />
BeginUpdate();<br />
<br />
Items.Clear();<br />
<br />
MySqlConnection connection = null;<br />
MySqlDataReader reader = null;<br />
<br />
try<br />
{<br />
... some code to get the reader ...<br />
<br />
reader = command.ExecuteReader();<br />
}<br />
catch (Exception)<br />
{<br />
}<br />
<br />
if (reader != null)<br />
{<br />
if (reader.HasRows)<br />
{<br />
while (reader.Read())<br />
{<br />
Items.Add(reader["primaryKey"].ToString());<br />
}<br />
}<br />
<br />
reader.Close();<br />
}<br />
<br />
if (connection != null)<br />
{<br />
connection.Close();<br />
}<br />
<br />
EndUpdate();<br />
}<br />
Still, the code is way to slow (why doesn't the thing already draw itself when I ask it to).
|
|
|
|
|
Geert van Horrik wrote: Still, the code is way to slow (why doesn't the thing already draw itself when I ask it to).
Because it's not handled if you only instanciated.
You will have to Show it (behinde the splash in your case), to create the handles!
You could Close it as soon as OnPaint is called.
So I would override Onpaint and set an init flag for the first time!
All the best,
Martin
|
|
|
|
|
Hi Martin,
I am not sure when and how Geert organized the initialization.
It may be OK, it may be sequential, depends on the code.
I cant tell when LoadData() gets called, I hope inside the form's
constructor.
As a consequence I am unsure what the handles got to do with it;
also you can enforce the handles to be generated by doing something
that needs them, not necessarily painting the form. So I dont feel
like fiddling with OnPaint.
My advise is to add logging with timestamps, so Geert has the
facts on which he can then proceed.
|
|
|
|
|
But the combobox is already being filled during the splashscreen, so the db code is handled BEFORE I even call the Show function of the form.
But, then the showing of the combobox takes a long time.
|
|
|
|
|
That's what I assumend and what Luc missunderstood (at least I think so).
I don't think that the filling of the combobox is the bottleneck, I think the painting itself takes long for the first time!
All the best,
Martin
|
|
|
|
|
Exactly!
However, I can't show the main form in the splash screen since that is handled in a separate thread (and it is giving me some (understandble and logical) dragdrop errors).
Best regards,
|
|
|
|
|
Geert van Horrik wrote: However, I can't show the main form in the splash screen since that is handled in a separate thread
If you use the ApplicationContext class, like in this article from jconwell[^], you can!
Hope it helps!
All the best,
Martin
|
|
|
|
|
Thank you
I will check out that article and implement it that way!
Thanks!
|
|
|
|
|
The problem is that I have created a SplashScreen control for our company. We simply have to derive a new class and override the HandleSplash() function and the splashscreen will perform the other data.
In the constructor I create a new thread to perform initialization, so the SplashScreen stays updated correctly. So, the ApplicationContext will not work since I still have a different thread which will create the real instance of the mainform.
Best regards,
|
|
|
|
|
Why not instanciate the mainform and all other used classes in the SplashAppContext constructor.
From there you could inform the splash and also Show the mainform.
As long as the mainform isn't automaticaly Closed (over the OnPaint, like I said before), the Splash stays TopMost opened.
All the best,
Martin
|
|
|
|
|
Hello Geert,
I just did a little test with the sampleproject of the article I was giving you!
I placed a lot of ListBoxes and ComboBoxes filled with lotts of Items on the Form1.
If you then start the proram, you can see the delay after the splash is closed bevore the Form1 is shown.
I than changed the constructor of SplashAppContext to:
public SplashAppContext(Form mainForm, Form splashForm) : base(splashForm)
{
this.mainForm = mainForm;
mainForm.Show();
splashTimer.Tick += new EventHandler(SplashTimeUp);
splashTimer.Interval = 2000;
splashTimer.Enabled = true;
}
And added OnPaint to Form1 like this:
private bool init=true;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint (e);
if(init)
{
init=false;
this.Hide();
}
}
After that the Form1 shown very fast.
All the best,
Martin
|
|
|
|
|
I have been researching this problem too.
It seems that filling the Items property is the one tackling down. The debugger shows this code already being executed at the InitializeComponent function, but internally, I guess the combobox lists the items when it is first shown (bad habit btw).
What I am planning now is disabling the automatic updating and showing the screen and hide it immediately. Then, I manually call a function to populate the data which I hope can be done from within the splashscreen.
Best regards,
|
|
|
|
|
Let us know if it works out for you!
Good luck
All the best,
Martin
|
|
|
|
|
Now I am facing the Thread problem since I create a worker thread in the splashscreen that does all the initializing.
I guess it's just a thing I have to live with.
I will keep investigating this and let you know any progress updates!
|
|
|
|
|
Hello,
Maybe you should think about the concept of your splash again.
In my understanding the mainthread should do the initializing (also the mainform) and give the splash the informationions for the user.
If you are showing progressbars and other GUI stuff you would have to use Application.DoEvents().
All the best,
Martin
|
|
|
|
|
Good idea.
Currently, I have managed to fix the problem by invoking a InitializeMainForm method in the thread so it is handled in the main thread again.
Then, I wait until I fire the even mainFormInitialized. This seem to work, but I will definitely try your way too!
Best regards,
|
|
|
|
|
I have now moved everything to the main thread, and all problems are fixed.
Thanks for helping me out on this one!
Best regards,
|
|
|
|
|
Glad I could help!
Geert van Horrik wrote: Thanks for helping me out on this one!
All the best,
Martin
|
|
|
|
|
Hi Geert,
Geert van Horrik wrote: But the combobox is already being filled during the splashscreen
is that what you intend to happen, or do you have actual prove of it ?
BTW: any chance your LoadData() is executed more than once, i.e. the initialization is
maybe OK, but possibly you load the data again by mistake ??
Maybe we need to see more code to actually help you...
Regards
|
|
|
|
|
I am very sure since I add a breakpoint at the ListData of the ComboBox control. It is called during the splashscreen.
When I show the form finally with:
<br />
Application.Run(MainForm.GetInstance());<br />
The breakpoint never gets called again, so I am VERY sure the combobox is already listed. The problem is probably the first time drawing the combobox...
|
|
|
|
|
OK, but drawing a ComboBox is cheap. It is like a TextBox plus a Button (for the arrow);
it is not even interested in its own data unless you click the arrow to drop it down.
So I suspect other things, see my earlier messages (lower in the thread though).
|
|
|
|