For an unknown reason, VB6 doesn't interact the same way with UserControl than other object.
I have a class that require to hold a graphical interface, a user control and need to be set to be later used from the get method. I have try many thing like using the special class VBControlExtender but without any success.
Here is what I have so far:
Class that hold variables and the user control:
'...
Private WithEvents m_uGUI As VBControlExtender
Public Property Get GUI() As VBControlExtender
Set GUI = m_uGUI
End Property
Public Property Set GUI(ByVal uValue As VBControlExtender)
Set m_uGUI = uValue
End Property
'...
Call of the class that cannot compile:
Set myObject.GUI = new ucMyUserControl
Any idea?
From the help on this error (it mentions ListBox and Form, but the same applies to UserControls):
The New keyword can only be applied to
a creatable object... You tried to
instantiate an Automation object, but
it was not a creatable object. For
example, you tried to create a new
instance of a list box by specifying
ListBox in a statement like the
following: [sample code snipped]
ListBox and Form are class names, not
specific object names. You can use
them to specify that a variable will
be a reference to a certain object
type... But you can't use them to
instantiate the objects themselves in
a Set statement. You must specify a
specific object, rather than the
generic class name, in the Set
statement:
What you want to do is make an array of your UserControls and load new ones as you need them. Set the Index property of your UserControl to 0 to make it an array and then use the Load statement to create new instances:
Load ucMyUserControl(1)
Set myObject.GUI = ucMyUserControl(1)
When you need more just specify a new upper bound:
Load ucMyUserControl(2)
Load ucMyUserControl(3)
...
When you're done with them, unload them:
Unload ucMyUserControl(3)
Unload ucMyUserControl(2)
...
I believe VBControlExtender can only be used with dynamically added controls (i.e. Controls.Add) not intrinsic controls. Why can't you use ucMyUserControl as the type instead?
Related
In VB6 on a UserControl, I must use UserControl.MousePointer = vbDefault instead of Me.MousePointer = vbDefault. I can use Me.MousePointer on a Form (and Form.MousePointer doesn't work).
Why must I use UserControl.MousePointer instead of Me.MousePointer?
I mean literally the text "UserControl", not UserControl as the placeholder for another control name.
Me isn't what you seem to think it is. It is a reference to the current instance of the module you use it in, not "magic."
To get what you want you must add this property to the default interface of your UserControl, e.g.:
Option Explicit
Public Property Get MousePointer() As MousePointerConstants
MousePointer = UserControl.MousePointer
End Property
Public Sub Test()
MsgBox Me.MousePointer
End Sub
In VB6 Forms are a little different, probably as a holdover from 16-bit VB to make porting old code easier. These always seem to inherit from a hidden interface. This is defined in a type library you don't have access to since Microsoft did not release it as part of VB6. Attempting to query it typically comes up with an error like:
Cannot jump to 'MousePointer' because it is in the library 'Unknown10' which is not currently referenced
From this alone it seems likely that using Me always carries a small performance penalty. Instead of going directly to the module's procedures it appears to me that you are going through its default COM interface.
You'd have to inspect the compiled code to determine whether there is a performance penalty, and if so how much. I don't see this documented so otherwise we are just guessing about it.
In any case there is little reason to ever use Me unless you must in order to qualify something.
Crummy example but:
Option Explicit
Private mCharm As Long
Public Property Get Charm() As Long
Charm = mCharm
End Property
Public Property Let Charm(ByVal RHS As Long)
mCharm = RHS
'Maybe we do more here such as update the user interface or some
'other things.
End Property
Public Sub GetLucky(ByVal Charm As Long)
'Do some stuff.
Charm = Charm + Int(Rnd() * 50000)
'etc.
Me.Charm = Charm 'Here we use Me so we can assign to the property Charm.
End Sub
That's really about the only legitimate use for Me anyway: scoping to the desired namespace. Relying on it because in typing it brings up IntelliSense is just lazy.
If anything Forms are "broken" not UserControls.
Figured it out. It turns out that since a UserControl is an ActiveX control, VB6 does some magic for you. With a Form control, it's not an ActiveX control which is why the MousePointer property is accessible via Me, like you'd expect.
For UserControl's, the UserControl control you create in VB6 sits on another control - the ActiveX control. That ActiveX control is accessible via UserControl. Something like this:
class ActiveXControl
{
int MousePointer;
VBUserControl control;
}
class VBUserControl
{
}
class YourUserControl : VBUserControl
{
ActiveXControl UserControl;
// we must use UserControl.MousePointer
}
but for a Form, it's more like this:
class Form
{
int MousePointer;
}
class YourForm : Form
{
// we actually inherit from Form so we use Me.MousePointer
}
I have defined a class like this:
Class Foo
Public SomePublicProperty
Public Function init(p_somePublicProperty)
set init = Me
SomePublicProperty= p_somePublicProperty
End Function
End Class
And then consumed that in my global.asa Application_OnStart lke this:
Dim fooInstance
Set fooInstance = New Foo.init("Some data")
fooArray = Array(fooInstance)
Application("fooArray") = fooArray
Which works fine but when i get the value back out of the application store on another page i can't get at the property...
fooArray = Application("fooArray")
fooArray(0).SomePublicProperty 'This line returns an error - Object doesn't support this property or method
I have tried putting the class definition into the second page, but it doesn't help.
What have I missed?
I have just found this question. Am I right in assuming the same rule re serialization applies equally to the Application object? and so i shouldn't try and do this?
Unfortunately you can't do this, here is a the best explanation I could find as to why;
From Can I store VBScript class objects in a Session variable?
This is because VBS classes are NOT true classes. They are really just in-memory collections of info, and there is no way to guarantee (for example) that an instance that is stored in one page will even come close to matching the class definition in another page.
This is not the same as using Server.CreateObject() COM objects which can be stored and retrieved from both the Application and Session objects.
You have a couple of options;
Serialise the object yourself, in a structured string then use this to de-serialise the object when needed.
Create a COM wrapper for your VBScript class and stop using Class statement altogether. As COM objects can be stored in Application and Session objects this should work as long as the COM class is single threaded.
Convert your class into an Array and use this instead.
Useful Links
Application Object (IIS)
Setting the Scope of COM Objects in ASP Pages - Giving an Object Application Scope
How do I add the DataSource and DataField properties to my ActiveX control?
My UserControl:
(source: picofile.com)
(source: picofile.com)
From Creating An ActiveX Control That Is A Data Source:
Create a new ActiveX control project.
Set a reference in the project to the appropriate data library
through the Project, References menu dialog box.
Set the UserControl's DataSourceBehavior property to 1-vbDataSource.
Create property procedures for custom properties that programmers
will use to manipulate the data source control's connection to data.
Typically, you'll implement String properties such as ConnectString
(connection string to initialize a Connection object) and
RecordSource (string to hold the query to initialize the data in the
recordset). Create private variables to hold the values of each of
the properties. Create private constants to hold their initial
default values. Program the InitProperties, ReadProperties, and
WriteProperties event procedures to persist these properties.
If you want to expose the control's Recordset for other programmers
to manipulate, then you should create a custom property name,
RecordSet. Its type will be the appropriate Recordset type that you
plan to program for your control. You may choose to make it
read-only, in which case you only need to give it a Property Get
procedure. Declare a private object variable to hold its value using
WithEvents (this exposes the event procedures to other programmers).
Declare a Private variable of the appropriate connection type that
you plan to program for your control. It will not correspond to a
custom property, but it's necessary in order to host the Recordset.
Code the InitProperties, ReadProperties, and WriteProperties events
to properly manage and persist the values of the properties created
in the previous steps.
Program the UserControl's GetDataMember event procedure to
initialize a recordset and return it in the second parameter. You
will derive the Recordset either from information contained in
custom Private variables or from hard-coded information in the
GetDataMember event procedure itself (see the previous section for
an example). You should perform some errortrapping to ensure that
you do indeed have a valid connection.
Put code in the UserControl's Terminate event that will gracefully
close the data connection.
If you want to allow users to navigate data by directly
manipulating your UserControl, then put the appropriate user
interface on your UserControl along with the code to navigate the
Recordset variable.
Your new ActiveX control should now be ready to test as a
DataSource:Add a standard EXE project to the Project Group. Now,
making sure you've closed the designer for the UserControl, add an
instance of your new control to the standard EXE's form.
Manipulate any necessary custom properties (such as ConnectString
or RecordSource) that you may have put in your custom control.
Put one or more bindable controls in the test project and set their
DataSource property to point to the instance of your Data Source
Control. Set their DataField properties to point to fields from the
exposed Recordset.
I have 10 different test cases .I want to create different objects of SwfEdit, SwfButton etc just once say in function and then use those in different actions in QTP.
I tried creating a function and linked it to a test case,however it did not work.
So I am not sure what could be correct way to link all these objects across all the test cases.
If you insist on creating your objects in code instead of using the object repository, you'll need to store those objects in some type of global variable. A basic example might be for a function library:
' Declare your global variable to hold the object
Public MyObject
' Create your object from a function
Public Sub InitializeGlobalObject()
' Use Descriptive Programing to create your object
Set MyObject = Window("title:=something").Button("index:=0")
End Sub
This will allow you to create the object once and then refer to it by the variable
' Click the button
MyObject.Click
You may run into issues caching an object like this because it will tend to hold on to the last screen object that it matches, whereas the object repository will refresh the screen object each time you call it. You may need to call the 'Refresh' method on your object before you use it for the first time after it is displayed on the screen.
You should use object repository to add objects first if you are not intending to use descriptive language.
You should spy on each object and then add it.
I am Using Janus.Windows.GridEX control in My User interface.I am trying to bung list of objects.But it is not happening as and when the binding object is assigned to DataSource.
Soon after i assign the binding source ,if i check the row count it still shows 0,even though the binding data has list of objects.
The collection class which i am trying to bind implements ICollection
Here Mytype is a class which i have written.
if you use Gridex1.Datasource = Object
GridEX1.SetDataBinding(Object, "")
Gridex1.RetriveStructure()
also add
Implements IList to your class.
there are some default functions that the grid needs.
for example the property Count() to count your rows
see more info about Ilist http://msdn.microsoft.com/en-us/library/system.collections.ilist.aspx
The reason for this is because Janus GridEx's do not read from the datasource until the control is visible (i.e. the control has loaded). If you need to access the fields in the underlying datastructure, then you can do this through GridEx.Datasource. However, if you need access to bound grid fields like the RowCount, your only solution is access them during the Load event of the parent, or some event that fire's after the control has been setup.
Hope this helps,
Chris