[RESOLVED] Is it possible to make a ComboBox item non-selectable?
I have a ComboBox loaded with items. One of the items is a separator bar. I don't want the user to be able to select this item. How would you prevent them from doing so? Ideally I'd like some way to make it non-selectable or disabled (is there an API that does this?).
Sample CB items list
=========
1
2
3
--- <--- Here's the separator bar
4
5
6
What I'm currently doing is changing the user's selection when they select the separator bar item. It works but it's a bit kludgey.
TIA for your thoughts.
(BTW - This is a VBCCR16 control so I do have additional properties and methods available to me over the standard VB6 version but I don't think it makes a difference for this issue.)
Re: Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
I have a ComboBox loaded with items. One of the items is a separator bar. I don't want the user to be able to select this item. How would you prevent them from doing so? Ideally I'd like some way to make it non-selectable or disabled (is there an API that does this?).
Sample CB items list
=========
1
2
3
--- <--- Here's the separator bar
4
5
6
What I'm currently doing is changing the user's selection when they select the separator bar item. It works but it's a bit kludgey.
TIA for your thoughts.
(BTW - This is a VBCCR16 control so I do have additional properties and methods available to me over the standard VB6 version but I don't think it makes a difference for this issue.)
Could you post your code and explain what you mean by "kludgey"?
Re: Is it possible to make a ComboBox item non-selectable?
This is what I'm currently doing. It works but it's not ideal. With this approach, the user is able to select that CB item, the combo box text momentarily shows the separator line, then the app changes the selection. The end result of these actions is some flashing.
I was hoping to prevent the user from selecting the item in the first place.
Re: Is it possible to make a ComboBox item non-selectable?
You could certainly subclass the ComboBox and get it done. That would probably give you much cleaner looking results, but I'm not sure you want to dive into those complexities. Beyond what's been suggested above, I'm not sure you've got another option other than jumping into subclassing.
I subclass the VB6 ComboBox for a particular application (individually coloring the dropdown items, and also putting tooltips on each item). It's not terribly difficult to subclass a ComboBox, but it's far from trivial as well.
Good Luck,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: Is it possible to make a ComboBox item non-selectable?
Originally Posted by Elroy
You could certainly subclass the ComboBox and get it done. That would probably give you much cleaner looking results, but I'm not sure you want to dive into those complexities. Beyond what's been suggested above, I'm not sure you've got another option other than jumping into subclassing.
I subclass the VB6 ComboBox for a particular application (individually coloring the dropdown items, and also putting tooltips on each item). It's not terribly difficult to subclass a ComboBox, but it's far from trivial as well.
Is the subclassing code something you'd be able/willing to share?
Re: Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
Is the subclassing code something you'd be able/willing to share?
Ok, the attached project has all kinds of stuff in it you don't need, but it does have an example of ComboBox subclassing. I cut this code out of a much larger project that's been in production for many years.
When cutting it out, I did try and preserve a semblance of organization. You'll see that in the three BAS modules I provided. Again, there's a lot there you may not need, and could carefully trim out.
The ComboBox is in a UserControl (UC), as that's the only example I had. However, if you work through it, you could take that piece out. However, it's probably not a bad idea to just leave it as a UC, and mold mine into something that does what you're after.
Good luck with it,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: Is it possible to make a ComboBox item non-selectable?
Thanks Olaf and Elroy - I downloaded both your projects and played with the code. Ultimately I was able to use Elroy's code to change the forecolor of the separator item to a disabled text color. It's not actually disabled but at least visually it stands out as different from the rest so when the app auto-changes the selection, the user will have an idea of what's happening.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
It will probably be a few days, but maybe I'll take a look at it and add an "Enabled" property to each item in the dropdown. If I get to it, I'll post what I do in this thread.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
I'm having one problem that perhaps you guys with Owner drawn controls can help with... When I disable the combo, the text in the edit portion of the combo disappears. Apparently I'm supposed to do some custom drawing for this case? All other custom drawing stuff is working properly for this combobox except for this one case. I've looked at all the custom-drawn combobox examples I could find on the web and none seem to do any special handling for this case. They all look pretty much like what I have.
Any ideas?
I realize that this is a separate question but it is part of the original issue which Olaf and Elroy helped me with so I chose to keep the follow-up question here.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Hey AAraya,
Typically, I'd dive into this and take a look at it for you. However, I'm trying to wrap up some other loose ends before going on a two-week vacation. If it's not solved before I get back, I'll almost certainly take a look at it then. For now, I'm just here for quick hit-or-miss stuff.
In the meantime, you, or anyone else, is more than welcome to look at my code from post #9 to see what's going on.
Also, AAraya, you may do others a favor if you attached a small VB6 project that outlined the problem, possibly with some cropped screenshots as well. Or, if you'd like to get fancy, a screen-movie-capture.
Good Luck,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Hi Elroy,
I found the problem on my own. I needed to check for an ItemState of ODS_COMBOBOXEDIT and set the PenColor to either COLOR_WINDOWTEXT or COLOR_GRAYTEXT depending on the enabled property of the control.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Ok, procrastinating my packing. I went ahead and tweaked my UC combo and made it have the option of no-select items.
It's nice in that they don't show selected even when hovering over them in the dropdown, and then certainly don't select if you try and click on them.
There's actually two pieces to stopping them: 1) I had to suppress the Click event, and 2) I had to make sure the dropdown text didn't go into the edit portion of the ComboBox. But it's all done.
Project is attached. In this test project, the green items (with a prefix line) are marked as "no select". The red items are normal items.
I also recorded a short video:
Here's the line where you call AddItem:
Code:
Public Sub AddItem(sItem As String, Optional lColor As Long = -1&, Optional bClickable As Boolean = True)
The new option is basically the optional bClickable argument, which defaults to True.
Enjoy,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Elroy - you rock! I had been working on this myself this morning and had come up with a solution very similar to yours already. Looking over your project confirmed my approach. Thank you!
I really only have one issue that I'd like to solve right now. When the user selects a separator item with a mouse click, I suppress the click and take them back to the previously selected item. Works great! But when they scroll through the list via a keypress (up or down arrow), the behavior should be different - rather than taking them back to their previous selection, it should "skip" over to the next or previous non-separator item. I don't have that working yet. Currently I was only checking for combo selection change messages in the subclassing proc but I guess I'm going to have to determine if the selection changed due to mouse click or a key press event??? Do I have that right?
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Ahhh, I didn't think about that. I see that mine gets hung and won't skip over a no-select item when using the arrows. I know what it's doing. It's keeping track of the last valid selectable item and resetting to that. So, when down-arrowing (or up-arrowing), it just keeps re-selecting the previous item, never able to skip the no-select item and get beyond it.
I'll have to think about that one. It's not a trivial solution because of the edge conditions. This probably isn't the case for you, but it will need to behave correctly if a no-select item is at the bottom (or top) of the list. That's what I mean by edge condition. Also, it's possible to have two (or more) no-select items together. Again, might not get to it before vacation.
Take Care,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Correct, I don't have to worry about edge conditions in my case like you do. I just need to know if the CBN_SELECTION is caused by a mouse click or by an arrow key. Per MSDN documentation...
CBN_SELCHANGE notification code
Sent when the user changes the current selection in the list box of a combo box. The user can change the selection by clicking in the list box or by using the arrow keys.
But there's no mention of how to know which action caused it.
I'll continue to search for an answer as I'm sure there must be one - menus handle this properly so perhaps I need to look at some owner drawn menu stuff?
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Ok AAraya,
I'm going to post some code, that has arrow functionality. It's just for the modSubclassing (Subclassing.bas) module. All else is the same as I posted in post #16. I've done some testing, but more is probably needed. What I've tested is: empty list in combo, top item non-selectable, bottom item non-selectable, duplicate contiguous items non-selectable, all items non-selectable ... and that all seems to work fine.
The one bug I did find (not corrected) is that things go awry if you're using more than one of these things at the same time. Basically, those Static variables in the subclassing procedure get mucked. I've got procedures to deal with that (basically the SubclassExtraData, GetExtraData, and UnSubclassExtraData procedures that you can see [used in other cases of subclassing]), but I'm really running out of time. Plane leaving early tomorrow morning, and my wife is giving me strange looks because I'm not packing.
Ok, here's the new code for modSubclassing:
Code:
Option Explicit
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
' MODULE level stuff for general subclassing.
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
Public gbAllowSubclassing As Boolean ' Be sure to turn this on if you're going to use subclassing.
Dim bSetWhenSubclassing_UsedByIdeStop As Boolean ' Never goes false once set by first subclassing, unless IDE Stop button is clicked.
Private Const WM_DESTROY As Long = &H2& ' All other needed constants are declared within the procedures.
'
Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
Private Declare Function GetWindowSubclass Lib "comctl32.dll" Alias "#411" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, pdwRefData As Long) As Long
Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
Private Declare Function NextSubclassProcOnChain Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
' MODULE level stuff specific to individual subclassing needs.
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
Private Enum ExtraDataIDs
' These must be unique for each piece of extra data.
' They just give us 4 bytes each managed by ComCtl32.
ID_ForMaxSize = 1
End Enum
#If False Then ' Intellisense fix.
Dim ID_ForMaxSize
#End If
'
Private Type DRAWITEMSTRUCT
CtlType As Long
CtlID As Long
ItemID As Long
ItemAction As Long
ItemState As Long ' Bitflags: ODS_COMBOBOXEDIT = &h1000& (edit control being drawn).
' ODS_SELECTED = &h0001&
' ODS_DISABLED = &h0004&
' ODS_FOCUS = &h0010&
' ODS_NOACCEL = &h0100&
' ODS_NOFOCUSRECT = &h0200&
' Others, but they don't apply to combobox.
'
hWndItem As Long ' hWnd to the ComboBox.
hDC As Long
rcItem As RECT
ItemData As Long
End Type
'
Private Declare Function GetStockObject Lib "gdi32" (ByVal nIndex As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function SetDCBrushColor Lib "gdi32" (ByVal hDC As Long, ByVal colorref As Long) As Long
Private Declare Function SetDCPenColor Lib "gdi32" (ByVal hDC As Long, ByVal colorref As Long) As Long
Private Declare Function SetBkMode Lib "gdi32" (ByVal hDC As Long, ByVal nBkMode As Long) As Long
Private Declare Function SetTextColor Lib "gdi32" (ByVal hDC As Long, ByVal crColor As Long) As Long
Private Declare Function Rectangle Lib "gdi32" (ByVal hDC As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal y2 As Long) As Long
Private Declare Function DrawTextA Lib "user32" (ByVal hDC As Long, lpStr As Any, ByVal nCount As Long, lpRect As RECT, ByVal wFormat As Long) As Long
'
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
' Generic subclassing procedures (used in many of the specific subclassing).
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
Private Sub SubclassSomeWindow(hWnd As Long, AddressOf_ProcToSubclass As Long, Optional dwRefData As Long, Optional uIdSubclass As Long)
'
' In most cases, we just use hWnd for uIdSubclass. However, in certain cases, we will use the same subclass procedure on the same hWnd
' with different uIdSubclass values. For instance, if we want to "watch" a control, but we subclass the form to do this. We may have
' several controls on the form we wish to "watch", but the form's hWnd and the subclass procedure will be the same. Therefore, we can
' use different uIdSubclass values to keep it all straight.
'
' The subclass uniqueness is defined by pfnSubclass and uIdSubclass (2nd and 3rd arguments in API calls).
'
' This can be called AFTER the initial subclassing to update dwRefData.
'
If Not gbAllowSubclassing Then Exit Sub
'
If uIdSubclass = 0& Then uIdSubclass = hWnd
bSetWhenSubclassing_UsedByIdeStop = True
Call SetWindowSubclass(hWnd, AddressOf_ProcToSubclass, uIdSubclass, dwRefData)
End Sub
Private Sub SubclassExtraData(hWnd As Long, dwRefData As Long, ID As ExtraDataIDs)
' This is used solely to store extra data.
'
If Not gbAllowSubclassing Then Exit Sub
'
bSetWhenSubclassing_UsedByIdeStop = True
Call SetWindowSubclass(hWnd, AddressOf DummyProcForExtraData, ID, dwRefData)
End Sub
Private Function GetSubclassRefData(hWnd As Long, AddressOf_ProcToSubclass As Long, Optional uIdSubclass As Long) As Long
' This one is used only to fetch the optional dwRefData you may have specified when calling SubclassSomeWindow.
' Typically this would only be used by the subclassed procedure, but it is available to anyone.
If uIdSubclass = 0& Then uIdSubclass = hWnd
Call GetWindowSubclass(hWnd, AddressOf_ProcToSubclass, uIdSubclass, GetSubclassRefData)
End Function
Private Function GetExtraData(hWnd As Long, ID As ExtraDataIDs) As Long
Call GetWindowSubclass(hWnd, AddressOf DummyProcForExtraData, ID, GetExtraData)
End Function
Private Function IsSubclassed(hWnd As Long, AddressOf_ProcToSubclass As Long, Optional uIdSubclass As Long) As Boolean
' This just tells us we're already subclassed.
Dim dwRefData As Long
If uIdSubclass = 0& Then uIdSubclass = hWnd
IsSubclassed = GetWindowSubclass(hWnd, AddressOf_ProcToSubclass, uIdSubclass, dwRefData) = 1&
End Function
Private Sub UnSubclassSomeWindow(hWnd As Long, AddressOf_ProcToSubclass As Long, Optional uIdSubclass As Long)
' Only needed if we specifically want to un-subclass before we're closing the form (or control),
' otherwise, it's automatically taken care of when the window closes.
'
' Be careful, some subclassing may require additional cleanup that's not done here.
If uIdSubclass = 0& Then uIdSubclass = hWnd
Call RemoveWindowSubclass(hWnd, AddressOf_ProcToSubclass, uIdSubclass)
End Sub
Private Sub UnSubclassExtraData(hWnd As Long, ID As ExtraDataIDs)
Call RemoveWindowSubclass(hWnd, AddressOf DummyProcForExtraData, ID)
End Sub
Private Function ProcedureAddress(AddressOf_TheProc As Long) As Long
' A private "helper" function for writing the AddressOf_... functions (see above notes).
ProcedureAddress = AddressOf_TheProc
End Function
Private Function DummyProcForExtraData(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
' Just used for SubclassExtraData (and GetExtraData and UnSubclassExtraData).
If uMsg = WM_DESTROY Then Call RemoveWindowSubclass(hWnd, AddressOf_DummyProc, uIdSubclass)
DummyProcForExtraData = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
End Function
Private Function AddressOf_DummyProc() As Long
AddressOf_DummyProc = ProcedureAddress(AddressOf DummyProcForExtraData)
End Function
Private Function IdeStopButtonClicked() As Boolean
' The following works because all variables are cleared when the STOP button is clicked,
' even though other code may still execute such as Windows calling some of the subclassing procedures below.
IdeStopButtonClicked = Not bSetWhenSubclassing_UsedByIdeStop
End Function
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
'
' The following are our functions to be subclassed, along with their AddressOf_... function.
' All of the following should be Private to make sure we don't accidentally call it,
' except for the first procedure that's actually used to initiate the subclassing.
'
'**************************************************************************************
'**************************************************************************************
'**************************************************************************************
Public Sub SubclassForColorCombos(CtlHwnd As Long, TheObjPtr As Long)
'
' This is used in conjunction with the ComboReplaceWithColor procedure.
' See notes in that ComboReplaceWithColor procedure for more details.
'
SubclassSomeWindow CtlHwnd, AddressOf ColorCombo_Proc, TheObjPtr
End Sub
Private Function ColorCombo_Proc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
If uMsg = WM_DESTROY Then
UnSubclassSomeWindow hWnd, AddressOf_ColorCombo_Proc, uIdSubclass
ColorCombo_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
Exit Function
End If
If IdeStopButtonClicked Then ' Protect the IDE. Don't execute any specific stuff if we're stopping. We may run into COM objects or other variables that no longer exist.
ColorCombo_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
Exit Function
End If
'
Static sPrevText As String
Static iPrevIdx As Long
Static bPrevSet As Boolean
'
Dim uDrawItem As DRAWITEMSTRUCT
Dim oBrush As Long
Dim oThePen As Long
Dim iRet As Long
Dim sText As String
Dim clr As ComboBoxColor
Const ColorMask As Long = &H80FFFFFF ' As a note, for these colors, 7 bits of the high byte should always be zero (or available for other flags). Only the sign bit is used (to indicate system colors).
Const NotClickableFlag As Long = &H1000000 ' As a note, for these colors, 7 bits of the high byte should always be zero (or available for other flags). Only the sign bit is used (to indicate system colors).
Dim cbn As Integer ' On WM_COMMAND, passed in the high-word of wParam.
Dim idx As Long
Dim ItemData As Long
Dim bUpArrow As Boolean
Dim bDnArrow As Boolean
'
Const WM_DRAWITEM As Long = &H2B&
Const ODT_COMBOBOX As Long = 3&
Const DC_PEN As Long = 19&
Const DC_BRUSH As Long = 18&
Const TRANSPARENT As Long = 1&
Const COLOR_WINDOW As Long = 5&
Const COLOR_WINDOWTEXT As Long = 8&
Const COLOR_HIGHLIGHT As Long = 13&
Const COLOR_HIGHLIGHTTEXT As Long = 14&
Const CB_GETLBTEXT As Long = &H148&
Const CB_GETLBTEXTLEN As Long = &H149&
Const DT_SINGLELINE As Long = &H20&
Const DT_VCENTER As Long = &H4&
Const DT_NOPREFIX As Long = &H800&
Const ODS_SELECTED As Long = &H1&
Const ODS_COMBOBOXEDIT As Long = &H1000& ' (edit control being drawn).
Const WM_SETCURSOR As Long = &H20&
Const WM_COMMAND As Long = &H111&
Const CBN_SELCHANGE As Integer = 1 ' This stops the click event.
Const CB_GETCURSEL As Long = &H147&
'
Select Case uMsg
Case WM_SETCURSOR ' Mouse-Move.
Set clr = ComObjectFromPtr(dwRefData)
clr.MouseMoveOnText
Set clr = Nothing
'
Case WM_COMMAND
GetMem2 ByVal (VarPtr(wParam) + 2&), cbn
If Not bPrevSet Then iPrevIdx = -1&: bPrevSet = True
Set clr = ComObjectFromPtr(dwRefData)
idx = clr.ListIndex
ItemData = clr.ItemData(idx)
'
If (ItemData And NotClickableFlag) = 0& Then
iPrevIdx = idx
Else
' If we're in here, we're on a non-clickable item.
If cbn = CBN_SELCHANGE Then ' We don't need to mess with others.
bUpArrow = GetKeyState(vbKeyLeft) < 0 Or GetKeyState(vbKeyUp) < 0
bDnArrow = GetKeyState(vbKeyRight) < 0 Or GetKeyState(vbKeyDown) < 0
If bUpArrow Or bDnArrow Then
If bUpArrow Then
Do
idx = idx - 1&
If idx < 0& Then Exit Do
ItemData = clr.ItemData(idx)
If (ItemData And NotClickableFlag) = 0& Then Exit Do
Loop
If idx < 0& Then
clr.ListIndex = iPrevIdx ' This stops the edit portion from changing.
Exit Function ' This prevents the click event.
Else
clr.ListIndex = idx
iPrevIdx = idx
End If
Else ' Down arrow.
Do
idx = idx + 1&
If idx >= clr.ListCount Then Exit Do
ItemData = clr.ItemData(idx)
If (ItemData And NotClickableFlag) = 0& Then Exit Do
Loop
If idx >= clr.ListCount Then
clr.ListIndex = iPrevIdx ' This stops the edit portion from changing.
Exit Function ' This prevents the click event.
Else
clr.ListIndex = idx
iPrevIdx = idx
End If
End If
Else
' We've just click on a non-clickable item with mouse.
clr.ListIndex = iPrevIdx ' This stops the edit portion from changing.
Exit Function ' This prevents the click event.
End If
End If
End If
'
Set clr = Nothing
'
Case WM_DRAWITEM
CopyMemory uDrawItem, ByVal lParam, Len(uDrawItem)
uDrawItem.ItemData = uDrawItem.ItemData
'
If uDrawItem.CtlType = ODT_COMBOBOX Then
'
' Get brush and pen.
oBrush = SelectObject(uDrawItem.hDC, GetStockObject(DC_BRUSH))
oThePen = SelectObject(uDrawItem.hDC, GetStockObject(DC_PEN))
'
If (uDrawItem.ItemState And ODS_SELECTED) And ((uDrawItem.ItemData And NotClickableFlag) = 0&) Then
' Draw highlight color rectangle.
SetDCBrushColor uDrawItem.hDC, GetRgbSystemColor(COLOR_HIGHLIGHT)
SetDCPenColor uDrawItem.hDC, GetRgbSystemColor(COLOR_HIGHLIGHT)
Rectangle uDrawItem.hDC, uDrawItem.rcItem.Left, uDrawItem.rcItem.Top, uDrawItem.rcItem.Right, uDrawItem.rcItem.Bottom
' And now setup for drawing text.
SetDCPenColor uDrawItem.hDC, GetRgbSystemColor(COLOR_HIGHLIGHTTEXT)
SetTextColor uDrawItem.hDC, GetRgbSystemColor(COLOR_HIGHLIGHTTEXT)
Else
' Draw regular color rectangle.
If (uDrawItem.ItemState And ODS_COMBOBOXEDIT) Then ' This is the actual edit box.
SetDCBrushColor uDrawItem.hDC, cboBackColorFromHwnd(uDrawItem.hWndItem)
SetDCPenColor uDrawItem.hDC, cboBackColorFromHwnd(uDrawItem.hWndItem)
Else ' This is something in the list.
SetDCBrushColor uDrawItem.hDC, GetRgbSystemColor(COLOR_WINDOW)
SetDCPenColor uDrawItem.hDC, GetRgbSystemColor(COLOR_WINDOW)
End If
Rectangle uDrawItem.hDC, uDrawItem.rcItem.Left, uDrawItem.rcItem.Top, uDrawItem.rcItem.Right, uDrawItem.rcItem.Bottom
' And now setup for drawing text.
SetDCPenColor uDrawItem.hDC, uDrawItem.ItemData And ColorMask ' The color is stored in the ItemData.
SetTextColor uDrawItem.hDC, uDrawItem.ItemData And ColorMask
End If
'
' We do this so our DrawText works the way we want.
SetBkMode uDrawItem.hDC, TRANSPARENT
'
' Fetch the text.
sText = vbNullString
If uDrawItem.ItemID >= 0& Then
iRet = SendMessageA(uDrawItem.hWndItem, CB_GETLBTEXTLEN, uDrawItem.ItemID, ByVal 0&)
If iRet Then
sText = Space$(iRet + 1&)
iRet = SendMessageA(uDrawItem.hWndItem, CB_GETLBTEXT, uDrawItem.ItemID, ByVal sText)
sText = Left$(sText, iRet)
End If
End If
'
' Draw the text.
uDrawItem.rcItem.Left = uDrawItem.rcItem.Left + 2& ' Offset text by two pixels.
DrawTextA uDrawItem.hDC, ByVal sText, Len(sText), uDrawItem.rcItem, DT_VCENTER Or DT_SINGLELINE Or DT_NOPREFIX
'
' Clean-up.
SelectObject uDrawItem.hDC, oBrush
SelectObject uDrawItem.hDC, oThePen
'
' Fire the MouseMoveOnList event ... but only if we're in the list and on our selected item, and only if we're on a clickable item.
If (uDrawItem.ItemState And ODS_SELECTED) <> 0& And _
(uDrawItem.ItemState And ODS_COMBOBOXEDIT) = 0& And _
(uDrawItem.ItemData And NotClickableFlag) = 0& Then
If sText <> sPrevText Then
Set clr = ComObjectFromPtr(dwRefData)
clr.MouseMoveOnList sText ' Return the text from the list item.
Set clr = Nothing
sPrevText = sText
End If
End If
'
' Get out.
Exit Function
End If
End Select
'
' If we fell out, just proceed as normal.
ColorCombo_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
End Function
Private Function AddressOf_ColorCombo_Proc() As Long
AddressOf_ColorCombo_Proc = ProcedureAddress(AddressOf ColorCombo_Proc)
End Function
I bolded the area where the changes were made (but there are a few changes in the module level code as well). (Also the bolding doesn't show well, but it's in the Case WM_COMMAND area.) Also, I made that uMsg test a Select Case, as I got tired of looking at those multiple If statements.
Good Luck,
Elroy
Last edited by Elroy; Aug 7th, 2019 at 10:11 AM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
I just came in to post that I had the arrow key functionality working now and I see that you've already posted Elroy! You handled this differently than I did. I see that you check the keystates in the cbn = CBN_SELCHANGE message handler. I did this a little differently...
I created a separate handler for WM_KEYUP messages and check for wParam values of VK_UP or VK_DOWN and check if the next/previous item is selectable or not and handle accordingly. It's working well for me. I wonder if it makes more sense to keep it all in the CBN_SELCHANGE handler like you did however. Hmmm...
You're also processing left and right arrow keys but I'm only checking for up and down. Are left and right keys used to navigate up and down a list?
BTW - Thank you VERY MUCH for your time and efforts on this!
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
Are left and right keys used to navigate up and down a list?
When in a combo's dropdown, left is handled the same as up, and right is handled the same as down. I just tested in a regular combo to figure this out.
Also, I'm taking a break from packing, so I'll look into getting rid of those static variables, and handling that more appropriately.
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Ok, it's working much better now. You can have as many of the UCs as you want on a form.
I've attached a demo project.
There's still one small bug, and I'm not sure when I'll fix it. It doesn't crash anything, and it takes a certain set of circumstances to even find it:
You've got to populate the UC combo with all non-selectable items (fourth combo in the demo).
You've got to pull down the pulldown section with the mouse (little down triangle).
Then you've got to tap the down arrow.
Then, you've got to click off of that combo onto some other control.
When it wraps back up, the first non-selectable item's text will be in the combo's textbox. The click event doesn't fire, but the text is still there, which is wrong.
I tried tracking it down, but got a bit bogged down. I'll eventually get it going, but not right now.
I tested all other conditions fairly well, and it works pretty good.
Enjoy,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
FWIW, here's the adaption of the example I've mentioned earlier:
(with less code for the combo-ctl, modern subclassing, and nicer rendering-outputs which includes Icons):
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Hi Olaf, I saw that your VirutalCombo uses wqweto's modern subclassing(the last time you used The trick's subclassing). I have not had time to test wqweto's modern subclassing. I'd like to know, what are the advantages of wqweto's modern subclassing compared to The trick's subclassing? Thanks.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by dreammanor
Hi Olaf, I saw that your VirutalCombo uses wqweto's modern subclassing(the last time you used The trick's subclassing). I have not had time to test wqweto's modern subclassing. I'd like to know, what are the advantages of wqweto's modern subclassing compared to The trick's subclassing? Thanks.
You asked this question of Olaf but I'll chime in too. I recently switched to "The Trick's" subclasser in my program. I'm not doing anything too fancy with it - I'm catching time change notifications and also using it in this ComboBox to catch and process selection changed notifications. But it works great and is much easier to use than my previous methods. The fact that it's IDE-safe makes it a winner for me. I too am interested to hear if there are advantages to wqweto's version.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by dreammanor
...saw that your VirtualCombo uses wqweto's modern subclassing
(the last time you used The trick's subclassing)...
I'm not a fan of any thunking, because it's not easy to maintain (or "to get right", mistakes are easy to make even for experienced devs) -
furthermore this ASM-stuff will work only on x86-CPUs (so the code is not really portable - e.g. to Win10-On-ARM).
The RC5-lib for example does not contain any thunks or assembly-stuff.
So I'd have preferred to use the RC5-SubClasser - though for these smaller Demos,
(to make it RC5-independent), a thunked SubClasser was used to keep the CodeBase relatively clean...
I started with "tricks", because it used quite the same concept as the one from RC5 (raising Events for the WinProc).
It worked flawlessly, until I've introduced and loaded multiple Forms (each showing a different usage of the VirtualCombo).
And that (multiple Forms loading/unloading) caused "occasional crashes" in the IDE (not directly whilst running,
but later on - e.g. at Project-saving-time).
@the trick
I've not had the time to research what may be the cause for this - perhaps that "multiple forms scenario" gives you a hint already
(all Forms using a VirtualCombo, which in turn is using two SubClassing-instances per Combo-Control-instance).
I've uploaded a Demo with those Multiple-Forms here in the CodeBank: http://www.vbforums.com/showthread.p...=1#post5408731
So I've switched to wqwetos recent attempt at the task - and I had only one crash with it so far...
(and that was shortly after I've pasted extra-code into the cSubClass.cls, to add the Hooking,
as well as the OneShotTimer-thunks - the crash came also a bit later in the same IDE-session).
But that was the only one - and it behaved otherwise stable in all follow-up IDE-sessions, also
with the "multiple-forms-loaded/unloaded" scenario ...
(so I guess, it was my "editing-around" within the cSubClass-CodeModule itself, which may have caused the crash).
I've updated the Combo-Source in the above linked CodeBank-Demo slightly,
and would suggest to use that latest version from the CodeBank-thread (instead of the one posted here in this thread).
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Olaf, I'm taking a look at your RC5 subclasser and I have two questions.
1) Is it IDE safe?
2) Your WindowProc parameters are different than "the tricks" and the previous one I worked with. In those when I wanted to override the default message handling with my own code, I would set a return value = 0 and set a DefCall/Default to False. When I wanted the default message handling, I would set DefCall/Default to True. Your class does have a Result param but does not have a DefCall/Default parameter. Please advise how I allow the default message handling and how I override it with my own.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
Olaf, I'm taking a look at your RC5 subclasser and I have two questions.
1) Is it IDE safe?
2) Your WindowProc parameters are different than "the tricks" and the previous one I worked with. In those when I wanted to override the default message handling with my own code, I would set a return value = 0 and set a DefCall/Default to False. When I wanted the default message handling, I would set DefCall/Default to True. Your class does have a Result param but does not have a DefCall/Default parameter. Please advise how I allow the default message handling and how I override it with my own.
Thanks
After playing with the RC5 subclasser I figured out how to use it so I have the answers to my two questions.
1) NO
2) To bypass normal message handling set Result = 0. To allow normal message handling use the CallWindowProc method of your subclasser.
I had no success using your subclasser with a VBCCR ComboBox. Using "the Tricks" subclasser I am able to hook the combobox and receive all WM_COMMAND messages and CBN_SELCHANGED notifications. Using your subclasser to hook the combobox I received no WM_COMMAND messages. Hmmm. Nonetheless, since it's not IDE Safe I'll not likely use it. It's very inconvenient to not be able to stop my app from executing without getting a crash.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
Olaf, I'm taking a look at your RC5 subclasser and I have two questions.
1) Is it IDE safe?
It should be (to a certain degree - perhaps not as good as the thunking ones, but I consider it sufficient).
Half a year ago or so, I've hardened it a bit more again, after DexWerx brought up the topic of IDE-safety in a comparison...
(not sure what it was I was changing back then, I think it was "enforced entering of break-mode when caused by an error").
Just play around with it - StopButton-safety should be there at least - break-point-handling should work too.
Originally Posted by AAraya
2) Your WindowProc parameters are different than "the tricks" and the previous one I worked with. In those when I wanted to override the default message handling with my own code, I would set a return value = 0 and set a DefCall/Default to False. When I wanted the default message handling, I would set DefCall/Default to True. Your class does have a Result param but does not have a DefCall/Default parameter. Please advise how I allow the default message handling and how I override it with my own.
The following simple example (MouseEnter/MouseLeave) shows its usage ...
In short, you need to put a line at the end of the Event-handler (as shown below in magenta).
If you want to provide your own (non-default-handled) Results, you just set them and do an Exit Sub.
Code:
Option Explicit
Private Declare Function TrackMouseEvent& Lib "user32" (lpTrack As Any)
Private WithEvents SC As cSubClass, PB As VB.PictureBox
Private Sub Form_Load()
Set PB = Controls.Add("VB.PictureBox", "PB"): PB.Visible = True
Set SC = New_c.SubClass: SC.Hook PB.hWnd
End Sub
Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
Const WM_MOUSEMOVE = &H200, WM_MOUSELEAVE = &H2A3
Select Case Msg
Case WM_MOUSEMOVE: HandleMouseEnterOn PB
Case WM_MOUSELEAVE: HandleMouseLeaveOn PB
End Select
Result = SC.CallWindowProc(Msg, wParam, lParam)
End Sub
Private Sub HandleMouseEnterOn(Ctl As Object)
Dim T&(0 To 3): T(0) = 16: T(1) = 2: T(2) = Ctl.hWnd
TrackMouseEvent T(0)
Ctl.BackColor = vbGreen
End Sub
Private Sub HandleMouseLeaveOn(Ctl As Object)
Ctl.BackColor = vbRed
End Sub
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
1) NO
Nah - it can't be *that* bad...
What's your RC5-version? (as said, half a year ago I've hardened the SubClasser a bit)...
I'm using VB6-SP6 on an up-to-date Win10 - and within the SubClass-EventHandler:
- can set my own breakpoints (and can also end the IDE from such a break-point via Stop-Button)
- can put e.g. Dim a: a = 1 / 0 into the EventHandler (forcing an "error-break") and also call an IDE-Stop from there
I have absolutely no AddIns running in my IDE though...
Originally Posted by AAraya
I had no success using your subclasser with a VBCCR ComboBox.
Using "the Tricks" subclasser I am able to hook the combobox and receive all WM_COMMAND messages and CBN_SELCHANGED notifications.
Just played that through with the newest "CCRP 1.6 OCX-download" - and it works without any problems:
(the example prevents any change in the drop-down-selection of a ComboBoxW1-Control)
Code:
Option Explicit
Private WithEvents SC As cSubClass
Private Sub Form_Load()
ComboBoxW1.AddItem "Item 1"
ComboBoxW1.AddItem "Item 2"
ComboBoxW1.AddItem "Item 3"
Set SC = New_c.SubClass: SC.Hook ComboBoxW1.hWnd
End Sub
Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
Const WM_COMMAND = &H111&, CBN_SELCHANGE = 1
Select Case Msg
Case WM_COMMAND: If wParam \ 65536 = CBN_SELCHANGE Then Exit Sub
End Select
Result = SC.CallWindowProc(Msg, wParam, lParam)
End Sub
HTH
Olaf
Last edited by Schmidt; Aug 8th, 2019 at 06:34 PM.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by The trick
Thank you for the report. Can you provide an example that causes the error?
As said, the error was not really caused by a concrete action (a click or something) -
it happened "after some time working in the IDE" (IIRC when saving the project, which causes new Control-instancing) -
but only after I worked with more than a single Form in the IDE - loading/closing those Extra-forms ...code-typing... and repeating that cycle
(the secondary Forms shown as non-modal Windows atop a "leading form").
I've uploaded such a multi-form-example a few hours ago into the Codebank-thread
(although with cSubClass.cls now containing wqwetos code, only adapted to your Event-throwing mechanism).
You should be able though, to switch to the original SubClassing which used your SubClasser by:
- overwriting cSubClass.cls with the similarly named File from the original Zip in the Opener-Post of the Codebank-thread (containing your code)
- overwriting the ucVirtualCombo.ctl in the same way (with the older version)
- then adding the modCreateHook.bas from the old project into the newer project (which is now needed again)
- and finally commenting out all the Combo-related Code in fTest.frm (which is the starting-form - only that forms Combo made use of some enhancements in the newer ucVirtualCombo.ctl)
The last point above meaning, that in the new uploaded Projects fTest.frm,
only the CommandButton-array should remain active, whit this EventHandler:
Code:
Private Sub cmdShowForms_Click(Index As Integer)
With Forms.Add("fTest" & Index)
.Show , Me
End With
End Sub
The routine above is then able to start the 3 "older Forms" (which should work with the older ucVirtualCombo.ctl in conjunction with your cSubClass.cls).
I hope the outlined steps above will give you a working project... the "additional Form-loading" is
the very same thing I've done a few months ago, whilst still working with your SubClasser in the older Project
(which I've overwritten with the new stuff, hence only the original Zip in the Codebank remains, to "restore things" in a roughly similar way).
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by Schmidt
Nah - it can't be *that* bad...
What's your RC5-version? (as said, half a year ago I've hardened the SubClasser a bit)...
Olaf
Hi Olaf,
It didn't work for me at all. I used the same code as you did and as I used in both wqweto and trick's subclassers. Their subclassers caught the messages/notifications I wanted while yours didn't. I also crashed every time I used yours.
I am using an older version of the vbRichClient5.dll however - I'm on version 5.0.0.67 dated 11/7/2018. I'll update my RC5 dlls. Thanks for your time and input.
Re: [RESOLVED] Is it possible to make a ComboBox item non-selectable?
Originally Posted by AAraya
My apologies Olaf. As I look more closely at your example I see that I wasn't coding this properly. This seems to be an operator error.
Ah, Ok... well - despite that, I'd still recommend updating your current RC5-version
(which - as said - should show more tolerant behaviour, when you run into unhandled VB6-errors,
caused by UserCode within the SubClassing-EventHandler).