1 Attachment(s)
VB6 Simple Virtual ComboBox (OwnerDrawn)
As the title says, a simple approach to accomplish ownerdrawing from Data in external Data-Containers
in a "DropDown-scenario".
As usual with virtual (bound) Controls, they are internally lightweight, since the Drawing happens on the outside.
Nevertheless (depending on what the OwnerDraw-Event offers), a typical scenario
can usually be implemented in only a few lines of OwnerDraw-Handler-Code -
right on the Data of your exernal DataSource-Container (be that an Array, a Collection or a Recordset).
The implementation below is based on only about 140 Lines of UserControl-Code.
Feel free to swap the SubClasser (Tricks clsSubClass currently) to your own implementation, if you like...
And since "Multi-Select-DropDown-scenarios" are apparently "en vouge" these days,
the Control supports this as well - as the ScreenShot below shows:
http://vbRIchClient.com/Downloads/VirtualCombo.png
Ok, here's the Demo-Code: Attachment 160471
Have fun with it...
Edit: enhancement of the MinVisibleItems-Prop, to work also in non-manifested environments.
Edit2: MouseWheel-based Scrolling now updates the currently selected Item-under the Mouse + additional Event (MouseMoveOnItem, to address Hover-Areas within a given Item)
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Why, regardless of the setting of MinVisibleItems, always and only 4 items are displayed?
Both on IDE and compiled EXE.
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
gibra
Why, regardless of the setting of MinVisibleItems, always and only 4 items are displayed?
Both on IDE and compiled EXE.
It's a feature of the (newer) CommonControls...
Meaning, as soon as you run your IDE with appropriate "manifest-support",
or (for the compiled Binary) include a CommonControls-covering *.res file into your Project,
everything should look as in the ScreenShot...
Edit: I've now included two lines, which should ensure the feature in roughly the same way also in non-manifested environments.
... have uploaded a new Zip...
HTH
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Schmidt
Edit: I've now included two lines, which should ensure the feature in roughly the same way also in non-manifested environments.
... have uploaded a new Zip...
Thank, now work good.
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Very nice Olaf. Not quite as full-featured as PGBSoft's, but far less code to get it done. Definitely two thumbs up from me. :thumb::thumb:
Take Care,
Elroy
1 Attachment(s)
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
fast scrolling cause weird behavior (mouse on an item and click, but highlight and select anothet item).
Edited: look like it is Listbox standard behavior. We need a mouse move after fast scrolling.
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Jonney
fast scrolling cause weird behavior (mouse on an item and click, but highlight and select anothet item).
Edited: look like it is Listbox standard behavior. We need a mouse move after fast scrolling.
This behaviour is now addressed (the hovered-Item is now reflected, also in MouseWheel-scrollings, where the Mouse is not moved otherwise).
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Elroy
Very nice Olaf. Not quite as full-featured as PGBSoft's, but far less code to get it done. Definitely two thumbs up from me. :thumb::thumb:
Well, at the moment I cannot think of a scenario which won't be possible with that virtual Control
(and a whole lot of scenarios, which are impossible with non-virtual Controls, that do not support OwnerDrawing).
Not many VB6-Users are familiar with the concept of an external DataContainer - perhaps that's the reason, why so much stuff gets "crammed" into the Controls itself.
To give a few examples...:
- Sorting (if you choose e.g. an ADO-Rs as your external DataContainer, you have multiple-column-sorts easily available - stuff like that does not belong into a Control IMO)
- Searching (same thing as with Sorting - just use your own Find- or Filter-routines on your external DataContainer - e.g. an ADO-Rs already comes with appropriate methods)
- Grid-like, or even TreeView-like real Multi-Colum-rendering is easily possible - and only depends on your external DataSource and how you manage the States there...
Virtual Controls are far underused by the community IMO.
They not only offer advantages implementation-wise (smaller, easier maintainable codebase) - but usage- (or UserCode-)wise as well.
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
I added a button support, let's see if it is needed: while supporting F4, you can press the down arrow to open the list.
Private Sub oCB_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyDown And Not DroppedDownState Then
KeyCode = vbKeyF4
End If
End Sub
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Say Olaf,
I took a run at cutting this thing down to be more like the typical ComboBox (but with the addition of multi-select). However, somewhere in the process, I did something that started causing crashes. I'd show it to you, but I got frustrated and deleted it all, thinking I'd just start again.
For me, I'm not interested in the icons. However, I am interested in being able to AddNew and Delete items after things are initially setup. That seems to be where I got hung up before.
This whole thing is almost certainly in your head more than mine. If you were so motivated, it'd be cool if you provided a version that would do this. If not, I certainly get it, but it doesn't hurt to ask.
Take Care,
Elroy
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Elroy
Say Olaf,
I took a run at cutting this thing down to be more like the typical ComboBox (but with the addition of multi-select). However, somewhere in the process, I did something that started causing crashes. I'd show it to you, but I got frustrated and deleted it all, thinking I'd just start again.
For me, I'm not interested in the icons. However, I am interested in being able to AddNew and Delete items after things are initially setup. That seems to be where I got hung up before.
This whole thing is almost certainly in your head more than mine. If you were so motivated, it'd be cool if you provided a version that would do this. If not, I certainly get it, but it doesn't hurt to ask.
Take Care,
Elroy
Something like this?
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Elroy
I took a run at cutting this thing down to be more like the typical ComboBox (but with the addition of multi-select).
However, somewhere in the process, I did something that started causing crashes.
Hmm, not sure about the crashes (didn't experience any whilst developing the demo, so Tricks SubClasser seems indeed quite IDE-safe).
Quote:
Originally Posted by
Elroy
For me, I'm not interested in the icons. However, I am interested in being able to AddNew and Delete items after things are initially setup.
That seems to be where I got hung up before.
An important thing with Virtual-Controls (which throw an OwnerDraw-Event at you, that comes with a "Record-Index") is,
to ensure that the "DataCount" of your external DataSource is "made known" to the virtual Control, as soon as you change it.
(no matter if the external DataSource is an Array or a Recordset, or a Collection - always tell the Control the new Count as soon as you change it).
Quote:
Originally Posted by
Elroy
This whole thing is almost certainly in your head more than mine. If you were so motivated, it'd be cool if you provided a version that would do this. If not, I certainly get it, but it doesn't hurt to ask.
Ok, what about using a 4-Column ADO-Recordset as the external DataSource, which:
- is inversed-sorted by ID (just to show how easy that is, without implementing any Sorting-stuff inside the Control)
- which gets a new Record dynamically added on each Combo-DropDown-Event
- then supporting MultiSelects whilst being in a "Dropped-Down-Session"
- dynamically deleting all selected Records in the Combo-RollUp-Event
- rendering its output in a Grid-like 3-Column-View (with differing TextColors in each Grid-Cell and different Alignments)
- looking like that?
http://vbRichClient.com/Downloads/Vi...oRsBinding.png
To bring this to life in your own Form - the easiest way is:
- to download and unzip the latest Combo-Project "as it is" in Post #1.
- then adding an additional empty Form1
- changing the Project-Startup-Form to that Form1
- adding a new Virtual Combo to it, with its default-name ucVirtualCombo1
- finally adding an ADO-Reference to the Project - and paste the following code:
Code:
Option Explicit
Private Rs As New ADODB.Recordset 'our external DataSource for this little scenario
Private Sub Form_Load()
'prepare a free-standing (yet empty) ADO-Rs with four fields)
Rs.Fields.Append "ID", adInteger
Rs.Fields.Append "FirstName", adVarWChar, 4096
Rs.Fields.Append "LastName", adVarWChar, 4096
Rs.Fields.Append "Checked", adBoolean
Rs.Open
'prepare the Virtual Combo for Multi-Select-Mode
ucVirtualCombo1.MultiSelect = True
ucVirtualCombo1.ItemHeight = 23
End Sub
Private Sub AddRecord(ID, FirstName, LastName) 'small Helper, to add a new Record to the Rs
Rs.AddNew
Rs!ID.Value = ID
Rs!FirstName.Value = FirstName
Rs!LastName.Value = LastName
Rs.UpdateBatch
End Sub
Private Sub ucVirtualCombo1_DropDown() 'here is, where we add Items dynamically (to the external DataSource)
Static ID As Long: ID = ID + 1
AddRecord ID, "FirstName " & ID, "LastName " & ID
Rs.Sort = "ID Desc" 'just to show that this works, we apply an inverse Sorting on the external DataSource
ucVirtualCombo1.ListCount = Rs.RecordCount 'always make the new RecordCount known to the virtual Control, as soon as it changes
End Sub
Private Sub ucVirtualCombo1_RollUp() 'here is, where we delete Items (from the external DataSource)
Dim i As Long
For i = Rs.RecordCount To 1 Step -1
Rs.AbsolutePosition = i
If Rs!Checked Then Rs.Delete
Next
ucVirtualCombo1.ListCount = Rs.RecordCount 'again, make the new RecordCount known to the virtual Control
End Sub
Private Sub ucVirtualCombo1_ListMultiClick() 'here is, where we synchronize the checked-state (marking Records for deletion during a "Drop-Down-Session")
Rs.AbsolutePosition = ucVirtualCombo1.ListIndex + 1 '<- synchronize the Rs to the current (zerobased) Item-Index
Rs!Checked.Value = Not Rs!Checked.Value
End Sub
Private Sub ucVirtualCombo1_OwnerDraw(ByVal Index As Long, ByVal IsSelected As Boolean, ByVal IsComboItem As Boolean, Canvas As PictureBox, ByVal dx As Long, ByVal dy As Long)
Canvas.FontName = "Arial": Canvas.FontSize = 10
Canvas.Line (0, 0)-(dx, dy), IIf(IsSelected, RGB(212, 232, 255), Canvas.BackColor), BF
If Index = -1 Or IsComboItem Then 'draw the Combo-Item
ucVirtualCombo1.TextOut 2, 2, "Select Items to delete"
Else 'draw the current List-Item
Rs.AbsolutePosition = Index + 1 '<- synchronize the Rs to the current (zerobased) Item-Index
DrawTextCell Canvas, Rs!ID, vbRightJustify, 0, 0.2, vbRed, dx
DrawTextCell Canvas, Rs!FirstName, vbRightJustify, 0.2, 0.6, vbBlue, dx
DrawTextCell Canvas, Rs!LastName, vbLeftJustify, 0.6, 1, vbMagenta, dx
Canvas.Line (4, 4)-(dy - 5, dy - 5), vbBlack, B
If Rs!Checked Then Canvas.FontName = "WebDings": Canvas.FontSize = 13: ucVirtualCombo1.TextOut 3, 0, "a"
End If
End Sub
'a small helper, to render "Text within a given Table-Cell"
Private Sub DrawTextCell(Canvas As PictureBox, ByVal Txt$, Align As AlignmentConstants, ByVal xPerc1#, ByVal xPerc2#, ByVal Color&, ByVal dx&)
Const InnerSpace = 3
Dim CW As Long: CW = (xPerc2 - xPerc1) * dx - 2 * InnerSpace
Dim TW As Long: TW = Canvas.TextWidth(Txt)
Dim x0 As Long: x0 = xPerc1 * dx + InnerSpace + Choose(Align + 1, 0, CW - TW, (CW - TW) / 2)
Canvas.ForeColor = Color
ucVirtualCombo1.TextOut x0, InnerSpace, Txt
Canvas.Line (xPerc2 * dx, 0)-(xPerc2 * dx, ucVirtualCombo1.ItemHeight), &HDDDDDD
Canvas.ForeColor = vbBlack
End Sub
HTH
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Added keyboard DropDown functionality.
Not sure is this right way to do it, by exposing vb intrinsic combobox hwnd publicly? However code below.
ucVirtualCombo.ctl add keydown event to declaration section
Code:
Event KeyDown(KeyCode As Integer, Shift As Integer)
'and long variable for the oCB.hwnd
Code:
Private mcboHwnd As Long
Private Sub UserControl_Initialize... add following line after 'hWndLB = hComboLBox' line.
Code:
mcboHwnd = oCB.hWnd 'expose vb intrinsic combobox hwnd publicly.
...add following subroutine for the Keydown event.
Code:
Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
RaiseEvent KeyDown(KeyCode, Shift)
Me.Refresh
End Sub
..add public property for the Hwnd
Code:
Public Property Get cboHwnd() As Long
cboHwnd = mcboHwnd
End Property
fTest form... add to declaration section
Code:
Private Const CB_SHOWDROPDOWN As Long = &H14F
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
...add KeyDown routine.
Code:
Private Sub cboV_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeySpace Then
If cboV.DroppedDownState = False Then
KeyCode = 0 'eat keycode, item is not selected when dropped down.
Call SendMessage(cboV.cboHwnd, CB_SHOWDROPDOWN, 1&, ByVal 0&)
End If
End If
End Sub
Now space key opens selection list.
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
FYI, F4 is the official keyb shortcut for dropdown in std comboboxes.
cheers,
</wqw>
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
wqweto
FYI, F4 is the official keyb shortcut for dropdown in std comboboxes.
cheers,
</wqw>
Thanks, i know - but didn't thought that :ehh: so ctl needs keydown event only and handler. Idea for this is to getting focus by tabbing to it in a 'standard keyboard data entry way', then same space key can be used for dropdown and item selection.
[ECHO]
Event KeyDown(KeyCode As Integer, Shift As Integer)
Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
RaiseEvent KeyDown(KeyCode, Shift)
Me.Refresh
End Sub
[/ECHO]
[ECHO]
Private Sub cboV_KeyDown(KeyCode As Integer, Shift As Integer)
'Space key dropdown support
If KeyCode = vbKeySpace And cboV.DroppedDownState = False Then
KeyCode = vbKeyF4 'translate key to F4, for combobox dropdown.
End If
End Sub
[/ECHO]
1 Attachment(s)
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Here's a recent Update for the (slightly enhanced) Combo-Control (now using wqwetos subclasser).
The Demo now contains 4 isolated Demo-Forms:
- the startup-form shows treelike Grouping within the drop-down-area (group-entries being non-selectable)
- fTest2.frm shows a Grid-like drop-down (using an ADO-Rs as the DataSource)
- fTest3.frm shows the Multi-Language selection-scenario
- fTest4.frm contains a very simple "no fancy drawings" example
Attachment 170329
Olaf
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
How to get the flag of the selected country show in the textbox
Re: VB6 Simple Virtual ComboBox (OwnerDrawn)
Quote:
Originally Posted by
Ordinary Guy
How to get the flag of the selected country show in the textbox
First, I'd suggest to use the latest Zip-File from post #16 (the Country-Flag-example is in fTest3.frm).
Keep in mind, that this scenario is currently configured for multiple country-selections
(not really a common scenario, I know).
But the Code-addition (within the OwnerDraw-Event of fTest3.frm) would need to be this one:
Code:
Private Sub cboV_OwnerDraw(ByVal Index As Long, ByVal IsSelected As Boolean, ByVal IsComboItem As Boolean, Canvas As PictureBox, ByVal dx As Long, ByVal dy As Long)
With Canvas 'all Drawing-Output happens "Item-wise" on a Canvas-PicBox, which is passed from inside the Virtual-ComboControl
.FontName = "Arial": .FontSize = 10
Canvas.Line (0, 0)-(dx, dy), IIf(IsSelected, RGB(212, 232, 255), Canvas.BackColor), BF
If Index = -1 Or IsComboItem Then 'here we choose, to draw the "checked accumulation" when Index= -1 comes in
cboV.TextOut 32, 3, IIf(Len(GetCheckedCountries), GetCheckedCountries, "<Select multiple Countries>")
If Index = -1 Then
.PaintPicture LoadPicture(ResPath & Lst(1).Nam & ".gif"), 0, 0, 20, 15
.PaintPicture LoadPicture(ResPath & Lst(4).Nam & ".gif"), 3, 3, 20, 15
.PaintPicture LoadPicture(ResPath & Lst(7).Nam & ".gif"), 6, 6, 20, 15
Else
.PaintPicture LoadPicture(ResPath & Lst(cboV.ListIndex).Nam & ".gif"), 3, 3, 20, 15
End If
The new added lines are in blue.
As said, since this this laguage/country-thingy in fTest3.frm was originally thought as a "multi-select-demo",
I've simply rendered "multiple Flags" in case a selection was made (as you see in the 3 violet colored lines).
If you want single-country-selections only - then:
- please ensure to switch cboV.MultiSelect = False (in Form_Load)
- and adapt the OwnerDraw-Event again - by "leaving more stuff out" (compared to above)
Code:
Private Sub cboV_OwnerDraw(ByVal Index As Long, ByVal IsSelected As Boolean, ByVal IsComboItem As Boolean, Canvas As PictureBox, ByVal dx As Long, ByVal dy As Long)
With Canvas 'all Drawing-Output happens "Item-wise" on a Canvas-PicBox, which is passed from inside the Virtual-ComboControl
.FontName = "Arial": .FontSize = 10
Canvas.Line (0, 0)-(dx, dy), IIf(IsSelected, RGB(212, 232, 255), Canvas.BackColor), BF
If Index = -1 Or IsComboItem Then 'here we choose, to draw the "checked accumulation" when Index= -1 comes in
'cboV.TextOut 32, 3, IIf(Len(GetCheckedCountries), GetCheckedCountries, "<Select multiple Countries>")
If cboV.ListIndex = -1 Then
cboV.TextOut 32, 3, "<Select a Country>"
' .PaintPicture LoadPicture(ResPath & Lst(1).Nam & ".gif"), 0, 0, 20, 15
' .PaintPicture LoadPicture(ResPath & Lst(4).Nam & ".gif"), 3, 3, 20, 15
' .PaintPicture LoadPicture(ResPath & Lst(7).Nam & ".gif"), 6, 6, 20, 15
Else
cboV.TextOut 32, 3, Lst(cboV.ListIndex).Nam
.PaintPicture LoadPicture(ResPath & Lst(cboV.ListIndex).Nam & ".gif"), 3, 3, 20, 15
End If
In addition, you might comment out the CheckBox-rendering Lines in the same EventHandler as well
(those are not really needed anymore, when in Single-Select-Mode).
As with most of these virtual List- or Combo-Demos I've placed in the CodeBank,
they are adjustable quite easily within the OwnerDraw-Event itself - just find the right "if conditions",
to choose different Colors, Offsets or Icon-references (by Index, etc.).
Olaf