The ExPropertiesList control exports a property
called Interfaces, that helps you to get the list of implemented
interfaces. The MsgBox PropertiesList1.Interfaces(Me) displays a list
of interfaces that form implements.
The MultiPage control is implemented in the FM20.DLL
file. The control loses it's content because the window that hosts the
control is destroyed every time when the user changes the active page.
If you would like to check this issue you need to add a MultiPage
control to your project references, to insert a instance of MultiPage
control to your main form, and to use the following code:
Private Declare Function GetWindow _
Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias _
"GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, _
ByVal nMaxCount As Long) As Long
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2
Private Sub MultiPage1_Change()
' The MultiPage control has no hWnd property so we need to look for a window which
' class name is 'F3 Server 60000000'
Dim h As Long
h = GetWindow(Me.hwnd, GW_CHILD)
' Looks for the window that hosts ActiveX controls in a MultiPage control
While (h <> 0)
' Gets the window's class name
Dim strClass As String * 255
GetClassName h, strClass, 255
If (strClass Like "F3 Server 60000000*") Then
' We have found the handle of the MultiPage's window
' Displays the window that hosts any ActiveX control
Debug.Print GetWindow(h, GW_CHILD)
Exit Sub
End If
' Gets the next window in the form
h = GetWindow(h, GW_HWNDNEXT)
Wend
End Sub
Now, run the project and select pages into your MultiPage
control. You will see that the VB's immediate window displays
different numbers, each time when you select a new page. Each number
represents the handle of the window that hosts ActiveX controls in
the MultiPage control. The MultiPage control destroys it's child
window every time when a new page is activated. That means that any
ActiveX control that is contained by a page in the MultiPage control
will be destroyed each time when a new page is activated. How do I
fix that? You can do one of the followings: use the Microsoft Tabbed
Dialog Control ( tabctl.ocx ) instead MultiPage control, use
Visible property of your control to hide or show the control when a
page is activated, load the control's content during changing
the page.
The VB provides the statement 'withevents' that can be used in
order to notify you application when a runtime control fires an
event. For instance, the following sample shows how to create and
handle events for ExComboBox control. The project references needs
to include a reference to 'ExComboBox 1.0 Control Library'.
Option Explicit
Dim WithEvents combobox As EXCOMBOBOXLib.combobox
Private Sub combobox_SelectionChanged()
If (combobox.Items.SelectCount > 0) Then
Debug.Print "Selection changed to '" & _
combobox.Items.CellCaption(combobox.Items.SelectedItem(0), 0) & "'."
End If
End Sub
Private Sub Form_Load()
Dim cb As Object
' USE this code only if you are creating the control at runtime on the client's machine.
' The following code is NOT required if you are adding the control on your development machine.
' The control requires a runtime license key, if you are creating the control at runtime
' Each product has an unique runtime license key. The runtime license key is FREE of charge.
' Use the runtime license key you have received from us, instead of XXXXXXXX
Licenses.Add "Exontrol.ComboBox", "XXXXXXXX"
' Adds an ExComboBox control at runtime
Set cb = Controls.Add("Exontrol.ComboBox", "combobox")
' Sets the "combobox" variable being the control events handler
Set combobox = cb.object
' Updates the control
combobox.BeginUpdate
' The Height should be called, to set the control's drop down height.
cb.Height = 128 * Screen.TwipsPerPixelY
cb.Left = (ScaleWidth - cb.Width) / 2
' Sets few control properties
combobox.ColumnAutoResize = True
combobox.HeaderVisible = False
combobox.Columns.Add "1"
' Adds few items
Dim i As Long
With combobox.Items
For i = 0 To 15
.AddItem i
Next
End With
' Makes the control visible
cb.Visible = True
combobox.EndUpdate
End Sub
The VB Unload method unloads an object. The "Unload Me"
statement closes the form. When "Unload Me" statement is
called during in some events of the control, the VB closes the form,
and so the control is closed too. So, the control is closed and it's
deleted, but the VB still gives the control to the caller, and so an
error 361 could occur.
There are several options that you can apply in order
to fix this problem.
DoCmd: In MS Access, you should call DoCmd.Close command instead Unload
Me, such as: DoCmd.Close acForm, Me.Name
DoEvents: The Unload Me should be preceded by a DoEvents
call, if calling within a UserForm.
WM_CLOSE: Call "PostMessage
Me.hWnd, WM_CLOSE, 0, 0" or "SendMessage Me.hWnd,
WM_CLOSE, 0, 0", instead calling the
"Unload Me" statement. You need to declare the
PostMessage API and the WM_CLOSE constant like follows:
Private
Declare Function PostMessage Lib "user32" Alias "PostMessageA"
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,
ByVal lParam As Long) As Long
Private
Declare Function SendMessage Lib "user32" Alias "SendMessageA"
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,
ByVal lParam As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Const WM_CLOSE = &H10
With Ribbon1
SendMessage GetParent(GetParent(.hwnd)), WM_CLOSE, 0, 0
End With
SendKeys: Call the SendKeys "%{F4}", to close the current
form.
Timer. You need to insert a disabled timer
control to your form, and to call "Unload Me" statement
in the timer's Timer event. You need to replace the "Unload
Me" statement in the control's event, with Timer1.Enabled =
True.
See also the Q189812 article in your MSDN, BUG: Unloading a Modal Form's Parent Form Causes VB To Hang
And you got the error: "Compile error: Ambiguous name
detected: IOwnerDrawHandler"
Create and add to the project the Class1 class that implements the
EXCOMBOBOXLibCtl.IOwnerDrawHandler like follows:
Implements EXCOMBOBOXLibCtl.IOwnerDrawHandler
Private Sub IOwnerDrawHandler_DrawCell(ByVal hDC As Long, ByVal left As Long, _
ByVal top As Long, ByVal right As Long, ByVal bottom As Long, _
ByVal Item As Long, ByVal Column As Long, ByVal Source As Object)
' Draws the cell. The Source points to the exComboBox object
End Sub
Create and add to the project the Class2 class that implements the
EXGRIDLibCtl.IOwnerDrawHandler like follows:
Implements EXGRIDLibCtl.IOwnerDrawHandler
Private Sub IOwnerDrawHandler_DrawCell(ByVal hDC As Long, ByVal left As Long, _
ByVal top As Long, ByVal right As Long, ByVal bottom As Long, ByVal Item As Long, _
ByVal Column As Long, ByVal Source As Object)
' Draws the cell. The Source points to the exGrid object
End Sub
Private Sub IOwnerDrawHandler_DrawCellBk(ByVal hDC As Long, Options As Variant, _
ByVal left As Long, ByVal top As Long, ByVal right As Long, ByVal bottom As Long, _
ByVal Item As Long, ByVal Column As Long, ByVal Source As Object)
' Draws the cell's background. The Source points to the exGrid object
End Sub
So, in the main form we need the following declarations:
Dim c1 As New Class1
Dim c2 As New Class2
So, when we need to specify an owner draw cell in the exComboBox
control we need to something like following:
With ComboBox1
With .Items
Set .CellOwnerDraw(...) = c1
End With
End With
In the exGrid the code should look like:
With Grid1
With .Items
Set .CellOwnerDraw(...) = c2
End With
End With
When the user double-clicks the window's caption,
the form is maximized, but the Windows system still sends a
WM_LBUTTONUP message to the window from the cursor, and so a Click
event may occur. The following VB sample shows how to remove this
issue:
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type MSG
hwnd As Long
message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
Private Const WM_LBUTTONUP = &H202
Private Const PM_REMOVE = &H1
Private Sub Form_Resize()
Dim m As MSG
While PeekMessage(m, 0, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE)
Wend
End Sub
The idea is to post a WM_LBUTTONDOWN message using
the PostMessage API function like in the following sample:
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
Private Type POINTAPI
x As Long
y As Long
End Type
Private Const WM_LBUTTONDOWN = &H201
Private Sub doclick()
With Grid1
Dim p As POINTAPI
p.y = GetMessagePos() / 65536
p.x = GetMessagePos() Mod 65536
ScreenToClient .hwnd, p
Dim l As Long
l = p.y * 65536 + p.x
PostMessage Grid1.hwnd, WM_LBUTTONDOWN, 0, l
End With
End Sub
The sample uses the hWnd property of the exGrid control to retrieve the handle of the window where click should occur.
Use the keybd_event API function to simulate
pressing the CTRL key as in the following sample:
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Sub SetCTRLState(ByVal bPressed As Boolean)
keybd_event &H11, &H1D, 1 Or IIf(bPressed, 0, 2), 0
End Sub
Private Sub Form_Load()
With List1
.SingleSel = False
End With
End Sub
Private Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
SetCTRLState (True)
End Sub
Private Sub List1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
SetCTRLState (False)
End Sub
The sample uses the exontrol's eXList componet when
the SingleSel property is False, so it supports multiple selection.
Once the user presses the left mouse button, the sample simulates
pressing the CTRL key too, so the item's state selection is toggled ie
if the item is already selected another click will unselect it, and if
the item is not selected the item gets selected, without unselecting
the all items as would it happen without the trick.
Let's say that you need to change the caption of the
selected item when a text field loses the focus using a code like
follows:
Private Sub Text1_LostFocus()
With Tree1.Items
.CellCaption(.SelectedItem, 0) = Text1.Text
End With
End Sub
Private Sub Tree1_SelectionChanged()
With Tree1.Items
Text1.Text = .CellCaption(.SelectedItem, 0)
End With
End Sub
The VB environment controls the order of firing events. So, the
SelectionChanged event is fired first, when the user clicks the
control, and after that is fired the LostFocus event. This way, the
result is not what we expected, so we need to find a work around so
the order of the events should be LostFocus and then SelectionChanged.
This way the correct item is updated when the text field loses the
focus. The following code changes the order of the events, so the
LostFocus event is fired first and after the SelectionChanged event is
fired, if case.
Private Sub Tree1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
DoEvents
End Sub
The DoEvents method processes all windows messages currently in the message queue.
Drag-and-drop is one of the fundamental metaphors underlying the Microsoft�
Windows� family of operating systems. Users understand that some items can be
moved around by holding the mouse down on them, and that they'll get appropriate
visual feedback when they're over a spot where the item can be dropped. They
expect to be able to move data and images from one spot to another this way.
There are two steps required in order to be able to drag and drop
items/objects/bars from our /COM components as follows:
Beginning a Drag-and-Drop Operation
To begin a drag-and-drop operation, you have to set the control's OLEDropMode
property on 1 and to handle the OLEStartDrag / OLEDragOver event. The AllowedEffects
/ Effect parameter
must be set on 1 or 2, and you need to call the SetData method of the Data
parameter so you specify the data to be dragged.
Accepting Data From a Drag Operation
The control fires the OLEDragDrop event, when the user drags data to the
control. The OLEDragDrop event occurs when a drag-and-drop operation is in
progress (that is, some control has initiated a drag and drop operation) and the
cursor enters the control.
The following Access sample shows the requirements in red:
Private Sub Form_Load()
Grid1.OLEDropMode = exOLEDropManual
End Sub
Private Sub Grid1_OLEDragDrop(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Dim hI As HITEM
hI = CLng(Data.GetData(exCFText))
If (hI <> 0) Then
With Grid1
Dim c As Long, hit As HitTestInfoEnum
Dim h As Long
h = .ItemFromPoint(-1, -1, c, hit)
If (h <> 0) Then
.BeginUpdate
With .Items
.SetParent hI, h
.ExpandItem(h) = True
.SelectItem(hI) = True
End With
.EndUpdate
End If
End With
End If
End SubPrivate Sub Grid1_OLEStartDrag(ByVal Data As EXGRIDLibCtl.IExDataObject, AllowedEffects As Long)
With Grid1
Dim c As Long, hit As HitTestInfoEnum
Dim h As Long
h = .ItemFromPoint(-1, -1, c, hit)
If (h <> 0) Then
AllowedEffects = 2
Data.SetData h, exCFText
End If
End With
End Sub
The sample lets user moves an item from another by drag and drop using the
SetParent method of Items object. The OLEStartDrag event initiates the OLE
Drag-Drop event, by carrying the handle of the item being moved from a parent to
the item where the cursor is released.
The following VB6 sample shows how you can list the files being dropped:
Private Sub Form_Load()
With Grid1
.OLEDropMode = exOLEDropManual
End With
End Sub
Private Sub Grid1_OLEDragDrop(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
With Data.Files
If (.Count > 0) Then
Dim s As String
For i = 0 To .Count - 1
s = s + .Item(i) + vbCrLf
Next
MsgBox s
End If
End With
End Sub
Private Sub Grid1_OLEDragOver(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single, ByVal State As Integer)
If (Data.Files.Count > 0) Then
Effect = 1
End If
End Sub
Compile and build the EXE. Run the EXE, and drag and drop files from your
Windows Explorer. As soon as you drop files from the Windows Explorer, the
control displays the list of files you dragged.
An application is considered an isolated application if all of its components are side-by-side assemblies. A side-by-side assembly is a collection of resources?a group of DLLs, windows classes, COM servers, type libraries, or interfaces?available for an application to use at runtime. Typically, a side-by-side assembly is one to several DLLs.
Isolated COM allows your application to use ActiveX components without having to register them. The original vision of this was to allow copy deployment of the application, but Isolated COM has many benefits. You can have a private copy of the DLL without worrying that another application will install an older or newer copy that breaks your application. Isolated COM also allows you to successfully install and run on non-Administrator accounts.
The
solution is to include the control's manifest file to the application's
resource under 24( Manifest Resource Type ) with the identifier 1.
Generating the RES file:
Open Visual Studio, select File\New\File and choose: Native
Resource Template
Right-click the .rct iem and select the Add Resource ...
Add Resource dialog is opened, select Custom, and type 24, click
OK
Open the eXHelper tool, and select
the component whose manifest file you need to generate
Copy the entire generated manifest ( CTRL + A, CTRL + C )
Go Back to Visual Studio's Resource Template
Paste the content you copied to the clipboard to the Custom
resource you created (CTRL + V)
Click Properties, to you change the identifier of the resource
item from IDR_RT_MANIFEST1 to 1
Save the template, by pressing the CTRL + S, so the Save File As
dialog is opened
Select the 32-bit Resource File (*.res), from Save as type field
Change the name and location where the RES file to be saved ( for
instance, exg2antt.res ), so it points to your VB6 project folder
Click the Save button
Finally, you have generated the RES file that includes the
Resource Type 24/Manifest with the identifier 1, which contains the
assembly manifest file for the component
Include the RES file as resource to your project
Open the VB6 project,
Go to Add-Ins\Add-In Manager...
Load the VB6 Resource Editor ( Load Behavior Check )
Go to Project \ Add New Resource File ...
The Open A Resource File dialog is opened
Select the RES file you previously generated ( exg2antt.res )
Click Open, and so the Project will show a new item exg2antt.res
under the Related Documents
Generate the EXE file
Select the File \ Make Project1.exe ...
Change the name of the EXE to be generated and click OK
Save all, and close the VB6 project
Copy the DLL to project folder
The "System Error &H8007007E (-2147024770). The specified
module could not be found. " is generated if you run the EXE
file, and this is happen because the EXE file can not locate the DLL
you are using as Isolated COM
Copy the exg2antt.dll file ( or the DLL file you generated
manifest from ) to the project folder ( same folder as EXE file )
Now, the EXE file can be run, and this way the EXE runs the
component as Isolated COM
Now, copy the generated EXE and the exg2antt.dll to a client machine, and
run the EXE. It will work, as in this case, the application uses the
exg2antt as isolated, so it requires no registration ( regsvr32 ).
You can download it here
the VB6 project. In the Release folder, you can find the sample.exe
that uses the exg2antt.dll as isolated.
First, you need to open the Visual Basic editor
by doing any of the following:
Click the Database Tools \ Visual Basic or Press Alt + F11
Next, you need to insert the UserForm by doing the following:
Right Click the Visual Basic menu, and Select Customize
Go to Commands, and look for Insert
Click and Drag the UserForm to the Visual Basic menu
Close the Customize dialog, and press the newly "Insert
UserForm" button
Next, you need to add the control to the newly insert user form:
Right Click the Visual Basic menu, and Select Customize
Go to Commands, and look for Tools
Click and Drag the Additional Controls... to the Visual Basic
menu
Close the Customize dialog, and press the newly "Additional
Controls" button
The "Additional Controls" is opened, and check/select
the control you are about to add to the your user form. For
instance: "ExComboBox ActiveX Control"
Click OK, and locate the ToolBox panel
The ToolBox panel, should include a new icon for the newly
inserted control, click and drag this icon to the user form, and
so you have inserted the control to the user-form
Once you generated the EXE to use the side-by-side components, it may happen to get the following error:
The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.
if you run the EXEcutable. This may happen because when generated the EXE, an
extra-zero may be added to the end of the manifest as explained bellow.
You can use the Windows' Events Viewer, to check for the error you got as you
can see bellow:
So, you got an:
Activation context generation failed for "...". Error in manifest or policy file "..." on line xxx. Invalid Xml syntax.
We need to open the EXE using a resource editor ( using Visual Studio, or any
other resource editor ), and to inspect the RT_MANIFEST\1 as seen next:
for some reason, there were added some unnecessary zeros. After we remove the
zeros at the end of the RT_MANIFEST\1 resource we get:
Now, the EXE will run just fine, and will use the components as isolated ( no
registration required ).