0

当用户选中或取消选中选中组合框中的复选框时,我想防止下拉列表关闭。

我复制了一些 Microsoft 代码来创建一个选中的组合框。由于它不是开箱即用的,我做了一些定制。

这是我的代码:

    Imports System.ComponentModel
Imports System.Collections.ObjectModel

Public Class CheckedCombobox
    Inherits ComboBox
    Public Event ItemCheck(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs)

    <Browsable(False)> _
    Public Overloads ReadOnly Property Items() As ComboBox.ObjectCollection
        Get
            Return MyBase.Items
        End Get
    End Property

    Private WithEvents _ItemCollection As New ObservableCollection(Of String)
    Public Property ItemCollection As ObservableCollection(Of String)
        Get
            Return _ItemCollection
        End Get
        Set(value As ObservableCollection(Of String))
            _ItemCollection = value
        End Set
    End Property

    Private _ItemDictionary As New Dictionary(Of String, Boolean)
    Public ReadOnly Property ItemDictionary As Dictionary(Of String, Boolean)
        Get
            Return _ItemDictionary
        End Get
    End Property
    Public ReadOnly Property CheckedItemCollection As List(Of String)
        Get
            Return New List(Of String)(From item In ItemDictionary Where item.Value = True Select item.Key)
        End Get
    End Property
    Public ReadOnly Property UnCheckedItemCollection As List(Of String)
        Get
            Return New List(Of String)(From item In ItemDictionary Where item.Value = False Select item.Key)
        End Get
    End Property

    Public Sub setCheckState(ByVal key As String, ByVal checkstate As Boolean)
        _ItemDictionary(key) = checkstate
    End Sub

    Public Function getCheckState(ByVal key As String)
        Return (_ItemDictionary(key))
    End Function

    Public Sub New()
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawVariable
    End Sub

    Protected Overrides Sub OnCreateControl()
        MyBase.OnCreateControl()
        For Each item In ItemCollection
            _ItemDictionary.Add(item, False)
        Next
    End Sub

    Private Sub ItemsChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) Handles _ItemCollection.CollectionChanged

        Select Case e.Action

            Case Specialized.NotifyCollectionChangedAction.Add
                If e.NewStartingIndex = ItemDictionary.Count Then
                    _ItemDictionary.Add(e.NewItems(0), False)
                    MyBase.Items.Add(e.NewItems(0))
                End If

            Case Specialized.NotifyCollectionChangedAction.Remove

                _ItemDictionary.Remove(MyBase.Items(e.OldStartingIndex))
                MyBase.Items.RemoveAt(e.OldStartingIndex)

            Case Specialized.NotifyCollectionChangedAction.Move

                Dim _item As Object = MyBase.Items(e.OldStartingIndex)
                MyBase.Items.RemoveAt(e.OldStartingIndex)
                MyBase.Items.Insert(e.NewStartingIndex, _item)

            Case Specialized.NotifyCollectionChangedAction.Replace

                Throw New Exception("Not implemented yet!")

            Case Specialized.NotifyCollectionChangedAction.Reset

                Dim _checkeditems As New List(Of String)(CheckedItemCollection)

                MyBase.Items.Clear()
                MyBase.Items.AddRange(_ItemCollection.ToArray)

                _ItemDictionary.Clear()
                For Each item In _ItemCollection
                    _ItemDictionary.Add(item, _checkeditems.Contains(item))
                Next

        End Select
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
        e.DrawBackground()
        Dim p As Point = e.Bounds.Location

        If e.Index >= 0 Then
            p.Offset(1, 1)
            If getCheckState(MyBase.Items(e.Index)) Then
                CheckBoxRenderer.DrawCheckBox(e.Graphics, p, VisualStyles.CheckBoxState.CheckedNormal)
            Else
                CheckBoxRenderer.DrawCheckBox(e.Graphics, p, VisualStyles.CheckBoxState.UncheckedNormal)
            End If

            p.Offset(12, 0)
            e.Graphics.DrawString(MyBase.GetItemText(Me.Items(e.Index)), e.Font, New SolidBrush(e.ForeColor), p.X, p.Y)
        End If
        If e.State = DrawItemState.Selected Then
            e.DrawFocusRectangle()
        End If
        MyBase.OnDrawItem(e)
    End Sub

    Private Sub checkedChanged(ByVal index As Integer)
        Dim checked As Boolean = _ItemDictionary(MyBase.Items.Item(index))
        If checked Then
            _ItemDictionary(MyBase.Items.Item(index)) = False
            RaiseEvent ItemCheck(Me, New ItemCheckEventArgs(index, CheckState.Unchecked, CheckState.Checked))
        Else
            _ItemDictionary(MyBase.Items.Item(index)) = True
            RaiseEvent ItemCheck(Me, New ItemCheckEventArgs(index, CheckState.Checked, CheckState.Unchecked))
        End If
        Me.Invalidate()
    End Sub

    Private n As nWindow = Nothing
    Private Const WM_CTLCOLORLISTBOX As Integer = &H134
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)
        If m.Msg = WM_CTLCOLORLISTBOX Then
            If n Is Nothing Then
                n = New nWindow(Me)
                n.AssignHandle(m.LParam)
                AddHandler n.checkedChanged, AddressOf checkedChanged
            End If
        End If
    End Sub

    Private Sub CheckedCombobox_Click(sender As Object, e As System.EventArgs) Handles Me.SelectedIndexChanged
        Debugger.Break()
    End Sub
End Class

Public Class nWindow
    Inherits NativeWindow

    Private Const WM_LBUTTONDOWN As Integer = &H201

    Private _combobox As CheckedCombobox

    Public Event checkedChanged(ByVal index As Integer)

    Public Sub New(ByVal cb As CheckedCombobox)
        _combobox = cb
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_LBUTTONDOWN Then
            Dim itemHeight As Integer = _combobox.ItemHeight
            If New Point(m.LParam.ToInt32).Y \ itemHeight <= _combobox.Items.Count - 1 And New Point(m.LParam.ToInt32).Y \ itemHeight >= 0 Then
                If New Point(m.LParam.ToInt32).X >= 1 And New Point(m.LParam.ToInt32).X <= 11 Then
                    RaiseEvent checkedChanged(_combobox.SelectedIndex)
                End If
            End If
        End If
        MyBase.WndProc(m)
    End Sub

End Class
4

1 回答 1

0

下面的代码似乎可以正常工作。它完成了我打算做的事情。

(注:我只展示了改变的例程)

 Private Sub checkedChanged(ByVal index As Integer)
    Dim checked As Boolean = _ItemDictionary(MyBase.Items.Item(index))
    If checked Then
        _ItemDictionary(MyBase.Items.Item(index)) = False
        RaiseEvent ItemCheck(Me, New ItemCheckEventArgs(index, CheckState.Unchecked, CheckState.Checked))
    Else
        _ItemDictionary(MyBase.Items.Item(index)) = True
        RaiseEvent ItemCheck(Me, New ItemCheckEventArgs(index, CheckState.Checked, CheckState.Unchecked))
    End If
    Me.SelectedIndex = -1
End Sub



Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg = WM_LBUTTONDOWN Then
        Dim itemHeight As Integer = _combobox.ItemHeight
        If New Point(m.LParam.ToInt32).Y \ itemHeight <= _combobox.Items.Count - 1 And New Point(m.LParam.ToInt32).Y \ itemHeight >= 0 Then
            If New Point(m.LParam.ToInt32).X >= 1 And New Point(m.LParam.ToInt32).X <= 11 Then
                RaiseEvent checkedChanged(_combobox.SelectedIndex)
                Return
            End If
        End If
    End If
    MyBase.WndProc(m)
End Sub

我不能百分百确定不调用 MyBase.WndProc(),但我还没有注意到任何副作用。

于 2013-02-07T11:32:18.243 回答