Object/Structure References [modified]
-
I am working on quite a large form that has become very slow to load, so i'm looking to take off some of the controls and have them loading on a background worker. The most used controls will remain, and a stack will be set up of controls to load in the background, loading the most recently requested control next. As controls can only be added on the main thread there is also a queue of controls that have been loaded on the background worker and now need adding to the form. All of this works, the issue i have is it would be useful to keep a reference to each control, so you only load it once etc. To stop code replication it would be great if i can create an object by sending in the System.Type of the object i want to make, the controlCollection it should be added to, some data it can use and the class/form level referance you want set to the control when it has loaded. Most of this works, however, i cannot get the class level reference to point to object created on the background worker. I get the feeling it should be doable using byRef, but am not quite sure how. I'm thinking my C++ way of thinking is causing problems here, any advice would be appreciated. Sorry for the large amount of code being posted, think it is all needed to solve the problem.
Public Class MyClass
'class level declaration of control to load on the background worker
Private _addresses As uscAddresses = NothingPublic Function Addresses() As uscAddresses If \_addresses Is Nothing Then 'if its not been loaded we should put it on the top of the stack 'don't worry if it is already on the stack as it will not get loaded twice \_loadQueue.Push(New ComponentToLoad(GetType(uscAddresses), \_addresses, Nothing, tpAddresses)) 'if we are already running the background worker no need to start it a second time If Not bgwLoader.IsBusy Then bgwLoader.RunWorkerAsync() 'ideally won't need this next bit in the final solution While \_addresses Is Nothing 'Application.DoEvents() 'Threading.Thread.Sleep(100) End While End If Return \_addresses End Function Structure ComponentToLoad Public Type As System.Type 'type of object that we want to load '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Public Target As UserControl 'the class level reference to use for the created object '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
-
I am working on quite a large form that has become very slow to load, so i'm looking to take off some of the controls and have them loading on a background worker. The most used controls will remain, and a stack will be set up of controls to load in the background, loading the most recently requested control next. As controls can only be added on the main thread there is also a queue of controls that have been loaded on the background worker and now need adding to the form. All of this works, the issue i have is it would be useful to keep a reference to each control, so you only load it once etc. To stop code replication it would be great if i can create an object by sending in the System.Type of the object i want to make, the controlCollection it should be added to, some data it can use and the class/form level referance you want set to the control when it has loaded. Most of this works, however, i cannot get the class level reference to point to object created on the background worker. I get the feeling it should be doable using byRef, but am not quite sure how. I'm thinking my C++ way of thinking is causing problems here, any advice would be appreciated. Sorry for the large amount of code being posted, think it is all needed to solve the problem.
Public Class MyClass
'class level declaration of control to load on the background worker
Private _addresses As uscAddresses = NothingPublic Function Addresses() As uscAddresses If \_addresses Is Nothing Then 'if its not been loaded we should put it on the top of the stack 'don't worry if it is already on the stack as it will not get loaded twice \_loadQueue.Push(New ComponentToLoad(GetType(uscAddresses), \_addresses, Nothing, tpAddresses)) 'if we are already running the background worker no need to start it a second time If Not bgwLoader.IsBusy Then bgwLoader.RunWorkerAsync() 'ideally won't need this next bit in the final solution While \_addresses Is Nothing 'Application.DoEvents() 'Threading.Thread.Sleep(100) End While End If Return \_addresses End Function Structure ComponentToLoad Public Type As System.Type 'type of object that we want to load '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Public Target As UserControl 'the class level reference to use for the created object '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
This sounds a bit like you are trying to solve a basic simple problem with a really really really complicated solution. From the way you are trying to solve this problem now, I understand that even when the form is loaded completely, the user won't need all the controls all the time anyway. Wouldn't it be easier to just rethink your GUI design? No program should need so many buttons, text boxes, or whatever, that an average PC starts having trouble loading the interface. Even if you were to get your solution to work, your app would likely bring the PC to its knees anyway, by the time the background thread has finished loading all controls. Try to combine controls, and reuse them, or to move certain secondary functionality (and the accompanying controls) to its own form, which you then open with a single button or automatically, as a result of another action taken or choice made.
My advice is free, and you may get what you paid for.
-
This sounds a bit like you are trying to solve a basic simple problem with a really really really complicated solution. From the way you are trying to solve this problem now, I understand that even when the form is loaded completely, the user won't need all the controls all the time anyway. Wouldn't it be easier to just rethink your GUI design? No program should need so many buttons, text boxes, or whatever, that an average PC starts having trouble loading the interface. Even if you were to get your solution to work, your app would likely bring the PC to its knees anyway, by the time the background thread has finished loading all controls. Try to combine controls, and reuse them, or to move certain secondary functionality (and the accompanying controls) to its own form, which you then open with a single button or automatically, as a result of another action taken or choice made.
My advice is free, and you may get what you paid for.
While i understand your points, the application i am working on is a product that has been in development and use for many years, and i'm quite restricted in what i can do to change where information is displayed. I think the C++ example of what i want is something like this:
//main.cpp
#include <iostream>
#include <stdlib.h>
#include "objTest.h"void main()
{
int toSet = 0;
objTest middleMan = objTest();
middleMan.setRef(&toSet);
middleMan.set(10);
//pause the app before quiting
std::cout<<toSet;
char c;
std::cin>>c;
}
// objTest.h
#pragma onceclass objTest
{
public:
objTest(void);
~objTest(void);
void setRef(int* newRef)
{
myVal = newRef;
}
void set(int newVal)
{
*myVal = newVal;
}
private:
int* myVal;
};This is really quite simple and can be very powerful when used correctly, but i'm guessing not something that can be done in VB.
-
I am working on quite a large form that has become very slow to load, so i'm looking to take off some of the controls and have them loading on a background worker. The most used controls will remain, and a stack will be set up of controls to load in the background, loading the most recently requested control next. As controls can only be added on the main thread there is also a queue of controls that have been loaded on the background worker and now need adding to the form. All of this works, the issue i have is it would be useful to keep a reference to each control, so you only load it once etc. To stop code replication it would be great if i can create an object by sending in the System.Type of the object i want to make, the controlCollection it should be added to, some data it can use and the class/form level referance you want set to the control when it has loaded. Most of this works, however, i cannot get the class level reference to point to object created on the background worker. I get the feeling it should be doable using byRef, but am not quite sure how. I'm thinking my C++ way of thinking is causing problems here, any advice would be appreciated. Sorry for the large amount of code being posted, think it is all needed to solve the problem.
Public Class MyClass
'class level declaration of control to load on the background worker
Private _addresses As uscAddresses = NothingPublic Function Addresses() As uscAddresses If \_addresses Is Nothing Then 'if its not been loaded we should put it on the top of the stack 'don't worry if it is already on the stack as it will not get loaded twice \_loadQueue.Push(New ComponentToLoad(GetType(uscAddresses), \_addresses, Nothing, tpAddresses)) 'if we are already running the background worker no need to start it a second time If Not bgwLoader.IsBusy Then bgwLoader.RunWorkerAsync() 'ideally won't need this next bit in the final solution While \_addresses Is Nothing 'Application.DoEvents() 'Threading.Thread.Sleep(100) End While End If Return \_addresses End Function Structure ComponentToLoad Public Type As System.Type 'type of object that we want to load '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Public Target As UserControl 'the class level reference to use for the created object '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
Well, the other problem you have, besides loading way too many controls on a form, is performance when the form has to redraw itself. A large number of controls simply takes longer and longer to render and gets worse as you add controls. You cannot move this to a background thread. Back to your original problem, I don't really see this as a viable solution considering all controls have to be created on the UI thread. Creating them on a seperate thread causes problems for messages crossing thread boundries when they a dispatched from the app's message pump. You can very easily run into issues with your GUI that are funky and hard to replicated and diagnose.
A guide to posting questions on CodeProject[^]
Dave Kreskowiak Microsoft MVP Visual Developer - Visual Basic
2006, 2007, 2008
But no longer in 2009... -
While i understand your points, the application i am working on is a product that has been in development and use for many years, and i'm quite restricted in what i can do to change where information is displayed. I think the C++ example of what i want is something like this:
//main.cpp
#include <iostream>
#include <stdlib.h>
#include "objTest.h"void main()
{
int toSet = 0;
objTest middleMan = objTest();
middleMan.setRef(&toSet);
middleMan.set(10);
//pause the app before quiting
std::cout<<toSet;
char c;
std::cin>>c;
}
// objTest.h
#pragma onceclass objTest
{
public:
objTest(void);
~objTest(void);
void setRef(int* newRef)
{
myVal = newRef;
}
void set(int newVal)
{
*myVal = newVal;
}
private:
int* myVal;
};This is really quite simple and can be very powerful when used correctly, but i'm guessing not something that can be done in VB.
I understand your problem. In this light, I think you are right, your C++ way of thinking is causing you problems. I honestly don't know whether this can be done in VB or not, but I am certain VB was never meant for application control on that level. Considering that the GUI must be created on the main thread, and that all interaction between user and app also happens on the main thread, what exactly are you trying to achieve by loading the controls in groups? If you consider that a PC with a multi core CPU (which will actually handle two threads at the same time), should be strong enough to load your form the old way, and a single core processor will handle each thread alternatingly, so it won't really load any faster anyway, and loading controls in groups will limit the use of the loaded controls until all controls have been loaded? How about creating and placing groups of controls at runtime, upon some mouse-over event or something? A bit like MS Office or the windows start menu.
My advice is free, and you may get what you paid for.
-
I understand your problem. In this light, I think you are right, your C++ way of thinking is causing you problems. I honestly don't know whether this can be done in VB or not, but I am certain VB was never meant for application control on that level. Considering that the GUI must be created on the main thread, and that all interaction between user and app also happens on the main thread, what exactly are you trying to achieve by loading the controls in groups? If you consider that a PC with a multi core CPU (which will actually handle two threads at the same time), should be strong enough to load your form the old way, and a single core processor will handle each thread alternatingly, so it won't really load any faster anyway, and loading controls in groups will limit the use of the loaded controls until all controls have been loaded? How about creating and placing groups of controls at runtime, upon some mouse-over event or something? A bit like MS Office or the windows start menu.
My advice is free, and you may get what you paid for.
the advantage i am looking to gain is that the main form would load up quicker, and allow interaction with the initially loaded controls, the ones that are used most. The less used but still required can be loaded say on the tab they are on being selected, however this means there will always be a wailt when the tab is clicked on, even if its a small wait. If the controls are loaded in the background then they will be available. I agree that loading them when they are needed is a good solution, but if i could get them loaded while the app idles, or the user is doing other tasks it would just be a nice bonus. GUI items don't need to be created on the main thread, the only action that must occur on the main thread is adding the created control to say a tab page's control collection. This may be because i am not declaring them as with events, but the way the form is used is 95% for displaying data, there is very little user interaction with the form which is why i can load them on the background worker. Maybe its just me getting a little frustrated that VB won't let me implement whats in my head. Thanks for the advice though.
-
the advantage i am looking to gain is that the main form would load up quicker, and allow interaction with the initially loaded controls, the ones that are used most. The less used but still required can be loaded say on the tab they are on being selected, however this means there will always be a wailt when the tab is clicked on, even if its a small wait. If the controls are loaded in the background then they will be available. I agree that loading them when they are needed is a good solution, but if i could get them loaded while the app idles, or the user is doing other tasks it would just be a nice bonus. GUI items don't need to be created on the main thread, the only action that must occur on the main thread is adding the created control to say a tab page's control collection. This may be because i am not declaring them as with events, but the way the form is used is 95% for displaying data, there is very little user interaction with the form which is why i can load them on the background worker. Maybe its just me getting a little frustrated that VB won't let me implement whats in my head. Thanks for the advice though.
Jep, like I said, VB just isn't meant for control on that level. And if you are used to C++, and the level of control it affords, you are bound to feel some frustration every once in a while.
My advice is free, and you may get what you paid for.
-
I am working on quite a large form that has become very slow to load, so i'm looking to take off some of the controls and have them loading on a background worker. The most used controls will remain, and a stack will be set up of controls to load in the background, loading the most recently requested control next. As controls can only be added on the main thread there is also a queue of controls that have been loaded on the background worker and now need adding to the form. All of this works, the issue i have is it would be useful to keep a reference to each control, so you only load it once etc. To stop code replication it would be great if i can create an object by sending in the System.Type of the object i want to make, the controlCollection it should be added to, some data it can use and the class/form level referance you want set to the control when it has loaded. Most of this works, however, i cannot get the class level reference to point to object created on the background worker. I get the feeling it should be doable using byRef, but am not quite sure how. I'm thinking my C++ way of thinking is causing problems here, any advice would be appreciated. Sorry for the large amount of code being posted, think it is all needed to solve the problem.
Public Class MyClass
'class level declaration of control to load on the background worker
Private _addresses As uscAddresses = NothingPublic Function Addresses() As uscAddresses If \_addresses Is Nothing Then 'if its not been loaded we should put it on the top of the stack 'don't worry if it is already on the stack as it will not get loaded twice \_loadQueue.Push(New ComponentToLoad(GetType(uscAddresses), \_addresses, Nothing, tpAddresses)) 'if we are already running the background worker no need to start it a second time If Not bgwLoader.IsBusy Then bgwLoader.RunWorkerAsync() 'ideally won't need this next bit in the final solution While \_addresses Is Nothing 'Application.DoEvents() 'Threading.Thread.Sleep(100) End While End If Return \_addresses End Function Structure ComponentToLoad Public Type As System.Type 'type of object that we want to load '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Public Target As UserControl 'the class level reference to use for the created object '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
ok, i've got this all working now, i had to do something that i'm not massivly proud of, and i know its not very VB in its workings but here goes, just incase any one is in a similar situation. The issue i was having was that the reference in the structure would change to point at the newly created control, insted of pointing the class level object to point to the new control. I'm aware this is how things are designed to work, just lacks the ability to tell it how i want it working. So i wrapped the control in a class, like so.
Class uscHolder
Public ctrl As UserControl
Public Sub New()
ctrl = Nothing
End Sub
End ClassThat way the structure will not change its reference to the new object, that will always point at the class level object. That way setting the usercontrol in the wrapper class is applied every where. Thanks agian for all the pointers, once i have a problem in my mind though even if its not the solution i use in the long run it will bug me untill i can think up a solution. When coding nothing should ever be impossible is a mindset i'm stuck with.