Skip to content

Instantly share code, notes, and snippets.

@hilapon
Last active October 4, 2018 05:51
Show Gist options
  • Save hilapon/96f3ff059f45411c9c92 to your computer and use it in GitHub Desktop.
Save hilapon/96f3ff059f45411c9c92 to your computer and use it in GitHub Desktop.
CheckedComboBox のサンプル
Option Explicit On
Option Strict On
#Region "名前空間"
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Diagnostics
#End Region
''' <summary>
''' チェック付きコンボボックス
''' </summary>
''' <remarks></remarks>
Public Class CheckedComboBox
Inherits ComboBox
#Region "内部クラス"
''' <summary>
''' <see cref="CheckedComboBox">CheckedComboBox</see> のドロップダウンを表す内部クラス
''' </summary>
Protected Friend Class CheckedComboBoxDropdown
Inherits Form
#Region "内部クラス"
''' <summary>
''' カスタムチェックリストボックス
''' </summary>
Protected Friend Class CustomCheckedListBox
Inherits CheckedListBox
Private m_curSelIndex As Integer = -1
''' <summary>
''' コンストラクタ
''' </summary>
Public Sub New()
MyBase.New()
Me.SelectionMode = SelectionMode.One
Me.HorizontalScrollbar = True
End Sub
''' <summary>
''' キーダウン時のイベントハンドラ
''' </summary>
Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
Select Case e.KeyCode
Case Keys.Enter
DirectCast(Parent, CheckedComboBoxDropdown). _
OnDeactivate(New CheckedComboBoxEventArgs(EventArgs.Empty, True))
e.Handled = True
Case Keys.Escape
DirectCast(Parent, CheckedComboBoxDropdown). _
OnDeactivate(New CheckedComboBoxEventArgs(EventArgs.Empty, False))
e.Handled = True
Case Keys.Delete
' Delete は全てのチェックを解除, [Shift + Delete] は全てチェックします。
For i As Integer = 0 To Items.Count - 1
Me.SetItemChecked(i, e.Shift)
Next
e.Handled = True
End Select
MyBase.OnKeyDown(e)
End Sub
''' <summary>
''' マウス移動時のイベントハンドラ
''' </summary>
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
MyBase.OnMouseMove(e)
Dim index As Integer = Me.IndexFromPoint(e.Location)
If (index >= 0) AndAlso (index <> m_curSelIndex) Then
m_curSelIndex = index
Me.SetSelected(index, True)
End If
End Sub
End Class
#End Region
#Region "フィールド"
Private m_cclb As CustomCheckedListBox
Private m_checkedStateArr() As Boolean
Private m_dropdownClosed As Boolean = True
Private m_oldStrValue As String = String.Empty
Private m_parent As CheckedComboBox
#End Region
#Region "コンストラクタ"
''' <summary>
''' コンストラクタ
''' </summary>
''' <param name="parent">
''' <seealso cref="CheckedComboBox">CheckedComboBox</seealso>オブジェクト
''' </param>
Public Sub New(ByVal parent As CheckedComboBox)
Me.m_parent = parent
Me.InitializeComponent()
Me.ShowInTaskbar = False
' イベントハンドラを設定します。
AddHandler Me.m_cclb.ItemCheck, AddressOf Me.m_cclb_ItemCheck
End Sub
#End Region
#Region "プロパティ"
''' <summary>
''' ドロップダウンに関連付けられている
''' <see cref="CheckedListBox">CheckedListBox</see>オブジェクトを取得します。
''' </summary>
Public ReadOnly Property List() As CheckedListBox
Get
Return m_cclb
End Get
End Property
''' <summary>
''' ドロップダウンで現在選択された項目のインデックスを取得・設定します。
''' </summary>
Public Property SelectedIndex() As Integer
Get
Return m_cclb.SelectedIndex
End Get
Set(ByVal value As Integer)
If (m_cclb IsNot Nothing) Then
m_cclb.SelectedIndex = value
If (m_cclb.Visible = False) Then
m_parent.Text = GetCheckedItemsStringValue()
End If
End If
End Set
End Property
''' <summary>
''' 選択項目が変更されているか取得します。
''' </summary>
Public ReadOnly Property ValueChanged() As Boolean
Get
Dim newStrValue As String = m_parent.Text
If ((m_oldStrValue.Length > 0) AndAlso (newStrValue.Length > 0)) Then
Return (m_oldStrValue.CompareTo(newStrValue) <> 0)
Else
Return (m_oldStrValue.Length <> newStrValue.Length)
End If
End Get
End Property
#End Region
#Region "イベント"
''' <summary>
''' <see cref="CustomCheckedListBox.ItemCheck">
''' CustomCheckedListBox.ItemCheck</see>イベントを発生させます。
''' </summary>
Private Sub m_cclb_ItemCheck(ByVal sender As Object, ByVal e As ItemCheckEventArgs)
If (m_parent.ItemChecked IsNot Nothing) Then
m_parent.ItemChecked(sender, e)
End If
End Sub
''' <summary>
''' <see cref="Activated">Activated</see>イベントを発生させます。
''' </summary>
Protected Overrides Sub OnActivated(ByVal e As EventArgs)
MyBase.OnActivated(e)
m_dropdownClosed = False
m_oldStrValue = m_parent.Text
m_checkedStateArr = New Boolean(m_cclb.Items.Count - 1) {}
For i As Integer = 0 To m_cclb.Items.Count - 1
m_checkedStateArr(i) = m_cclb.GetItemChecked(i)
Next
End Sub
''' <summary>
''' <see cref="Deactivate">Deactivate</see>イベントを発生させます。
''' </summary>
Protected Overrides Sub OnDeactivate(ByVal e As EventArgs)
MyBase.OnDeactivate(e)
If (e IsNot Nothing) AndAlso (TypeOf e Is CheckedComboBoxEventArgs) Then
CloseDropdown(DirectCast(e, CheckedComboBoxEventArgs).AssignValues)
Else
CloseDropdown(True)
End If
End Sub
#End Region
#Region "メソッド"
''' <summary>
''' コンポーネントを初期化します。
''' </summary>
Private Sub InitializeComponent()
Me.m_cclb = New CustomCheckedListBox()
Me.SuspendLayout()
'
' m_cclb
'
Me.m_cclb.BorderStyle = System.Windows.Forms.BorderStyle.None
Me.m_cclb.Dock = System.Windows.Forms.DockStyle.Fill
Me.m_cclb.FormattingEnabled = True
Me.m_cclb.Location = New System.Drawing.Point(0, 0)
Me.m_cclb.Name = "m_cclb"
Me.m_cclb.Size = New System.Drawing.Size(47, 15)
Me.m_cclb.Font = DirectCast(m_parent.Font.Clone(), Font)
'
' Dropdown
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0F, 13.0F)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.BackColor = System.Drawing.SystemColors.Menu
Me.ClientSize = New System.Drawing.Size(47, 16)
Me.ControlBox = False
Me.Controls.Add(Me.m_cclb)
Me.ForeColor = System.Drawing.SystemColors.ControlText
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow
Me.MinimizeBox = False
Me.Name = "m_parent"
Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
Me.ResumeLayout(False)
End Sub
''' <summary>
''' ドロップダウンを閉じ、指定されたブールパラメタに応じて変更します。
''' <para>
''' 呼び出し元が変更を求める可能性があるにもかかわらず、確定されていなければ、
''' これは必ずしもすべての変更が発生したわけではありません。<br/>
''' 発信者は、任意の実際の値の変更を決定するために
''' <see cref="CheckedComboBox">CheckedComboBox</see>(ドロップダウンが閉じている)の
''' <see cref="ValueChanged">ValueChanged</see>プロパティを確認してください。
''' </para>
''' </summary>
''' <param name="enactChanges">変更確定フラグ</param>
Public Sub CloseDropdown(ByVal enactChanges As Boolean)
If (m_dropdownClosed) Then
Return
End If
If (enactChanges) Then
m_parent.SelectedIndex = -1
m_parent.Text = GetCheckedItemsStringValue()
Else
For i As Integer = 0 To m_cclb.Items.Count - 1
m_cclb.SetItemChecked(i, m_checkedStateArr(i))
Next
End If
m_dropdownClosed = True
m_parent.Focus()
m_parent.SelectionLength = 0
Me.Hide()
m_parent.OnDropDownClosed(New CheckedComboBoxEventArgs(EventArgs.Empty, False))
End Sub
''' <summary>
''' チェック項目の文字列を結合して取得します。
''' </summary>
Public Function GetCheckedItemsStringValue() As String
Dim sb As New System.Text.StringBuilder()
For i As Integer = 0 To m_cclb.CheckedItems.Count - 1
sb.Append(m_cclb.GetItemText(m_cclb.CheckedItems(i))).Append(m_parent.Separator)
Next
If (sb.Length > 0) Then
sb.Remove(sb.Length - m_parent.Separator.Length, m_parent.Separator.Length)
End If
Return sb.ToString()
End Function
#End Region
End Class
#End Region
#Region "イベントの宣言"
Public ItemChecked As ItemCheckEventHandler
#End Region
#Region "フィールド"
Private m_dropdown As CheckedComboBoxDropdown
Private m_separator As String
#End Region
#Region "コンストラクタ"
''' <summary>
''' コンストラクタ
''' </summary>
Public Sub New()
MyBase.New()
Me.DrawMode = DrawMode.OwnerDrawVariable
Me.Separator = ", "
Me.DropDownHeight = 1
Me.DropDownStyle = ComboBoxStyle.DropDown
Me.CheckOnClick = True
End Sub
#End Region
#Region "プロパティ"
''' <summary>
''' このコントロール内でチェックされているインデックスのコレクションを取得します。
''' </summary>
Public ReadOnly Property CheckedIndices() As CheckedListBox.CheckedIndexCollection
Get
Return m_dropdown.List.CheckedIndices
End Get
End Property
''' <summary>
''' このコントロール内でチェックされている項目のコレクションを取得します。
''' </summary>
Public ReadOnly Property CheckedItems() As CheckedListBox.CheckedItemCollection
Get
Return m_dropdown.List.CheckedItems
End Get
End Property
''' <summary>
''' 項目が選択されたときに、チェックボックスを切り替えるかどうかを示す値を取得または設定します。
''' </summary>
Public Property CheckOnClick() As Boolean
Get
If (m_dropdown IsNot Nothing) Then
Return m_dropdown.List.CheckOnClick
Else
Return Nothing
End If
End Get
Set(ByVal value As Boolean)
If (m_dropdown IsNot Nothing) Then m_dropdown.List.CheckOnClick = value
End Set
End Property
''' <summary>
''' このコントロールのデータソースを取得または設定します。
''' </summary>
''' <remarks>このプロパティは使用しないでください。実装すると
''' <see cref="NotImplementedException">NotImplementedException</see>
''' 例外が発生します。</remarks>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
Browsable(False)> _
_
Private Shadows Property DataSource() As Object
Get
Throw New NotImplementedException("このプロパティは使用できません。実装しないでください。")
End Get
Set(ByVal value As Object)
Throw New NotImplementedException("このプロパティは使用できません。実装しないでください。")
End Set
End Property
''' <summary>
''' このコントロールに表示するプロパティを取得または設定します。
''' </summary>
Public Shadows Property DisplayMember() As String
Get
If (m_dropdown IsNot Nothing) Then
Return m_dropdown.List.DisplayMember
Else
Return String.Empty
End If
End Get
Set(ByVal value As String)
m_dropdown.List.DisplayMember = value
End Set
End Property
''' <summary>
''' このコントロール内の項目のコレクションを取得します。
''' </summary>
Public Shadows ReadOnly Property Items() As CheckedListBox.ObjectCollection
Get
Return m_dropdown.List.Items
End Get
End Property
''' <summary>
''' 現在選択されている項目を指定しているインデックスを取得または設定します。
''' </summary>
Public Overrides Property SelectedIndex() As Integer
Get
If (m_dropdown IsNot Nothing) Then
Return m_dropdown.SelectedIndex
Else
Return -1
End If
End Get
Set(ByVal value As Integer)
If (m_dropdown Is Nothing) Then Return
m_dropdown.SelectedIndex = value
End Set
End Property
''' <summary>
''' <see cref="Text">Text</see> に表示される項目間のセパレータ文字を取得または設定します。
''' </summary>
Public Property Separator() As String
Get
Return m_separator
End Get
Set(ByVal value As String)
m_separator = value
End Set
End Property
''' <summary>
''' このコントロールに関連付けられているテキストを取得または設定します。
''' </summary>
Public Overrides Property Text() As String
Get
If (MyBase.Items.Count = 0) Then
Return String.Empty
End If
Return MyBase.Text
End Get
Set(ByVal value As String)
Try
MyBase.Text = value
Catch ex As ArgumentOutOfRangeException
' この例外は意図的にスルーする。
Catch ex As Exception
Throw
End Try
End Set
End Property
''' <summary>
''' コントロールの値が変更されたか取得します。
''' </summary>
Public ReadOnly Property ValueChanged() As Boolean
Get
Return m_dropdown.ValueChanged
End Get
End Property
''' <summary>
''' コントロール内の項目の実際の値として使用するプロパティを取得または設定します。
''' </summary>
Public Shadows Property ValueMember() As String
Get
If (m_dropdown IsNot Nothing) Then
Return m_dropdown.List.ValueMember
Else
Return String.Empty
End If
End Get
Set(ByVal value As String)
m_dropdown.List.ValueMember = value
End Set
End Property
#End Region
#Region "イベント"
''' <summary>
''' レイアウト変更時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnLayout(ByVal levent As LayoutEventArgs)
MyBase.OnLayout(levent)
If (m_dropdown Is Nothing) Then
m_dropdown = New CheckedComboBoxDropdown(Me)
End If
End Sub
''' <summary>
''' フォント変更時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnFontChanged(ByVal e As EventArgs)
MyBase.OnFontChanged(e)
Dim font As Font = DirectCast(Me.Font.Clone(), Font)
m_dropdown.Font = font
m_dropdown.List.Font = font
End Sub
''' <summary>
''' ドロップダウン時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnDropDown(ByVal e As EventArgs)
MyBase.OnDropDown(e)
Me.DoDropDown()
End Sub
''' <summary>
''' ドロップダウンを閉じた時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnDropDownClosed(ByVal e As EventArgs)
If (TypeOf e Is CheckedComboBoxEventArgs) Then
MyBase.OnDropDownClosed(e)
End If
End Sub
''' <summary>
''' キーダウン時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
If (e.KeyCode = Keys.Down) Then
OnDropDown(EventArgs.Empty)
End If
' 特定のキーまたは組合せが妨げられないことを確認します。
e.Handled = _
Not e.Alt AndAlso _
Not (e.KeyCode = Keys.Tab) AndAlso _
Not ( _
(e.KeyCode = Keys.Left) OrElse _
(e.KeyCode = Keys.Right) OrElse _
(e.KeyCode = Keys.Home) OrElse _
(e.KeyCode = Keys.End) _
)
MyBase.OnKeyDown(e)
End Sub
''' <summary>
''' キーが押された時のイベントを発生させます。
''' </summary>
Protected Overrides Sub OnKeyPress(ByVal e As KeyPressEventArgs)
e.Handled = True
MyBase.OnKeyPress(e)
End Sub
#End Region
#Region "メソッド"
''' <summary>
''' ドロップダウン処理を行います。
''' </summary>
Private Sub DoDropDown()
If (Not m_dropdown.Visible) Then
Dim rect As Rectangle = RectangleToScreen(Me.ClientRectangle)
m_dropdown.Location = New Point(rect.X, rect.Y + Me.Size.Height)
Dim count As Integer = m_dropdown.List.Items.Count
If (count > Me.MaxDropDownItems) Then
count = Me.MaxDropDownItems
ElseIf (count = 0) Then
count = 1
End If
m_dropdown.Size = New Size(Me.Size.Width, (m_dropdown.List.ItemHeight) * count + 2)
m_dropdown.Show(Me)
End If
End Sub
''' <summary>
''' 項目のチェック状況を取得します。
''' </summary>
''' <param name="index">インデックス</param>
''' <returns>チェックされていたら True</returns>
''' <remarks>index がリストの範囲外の場合、
''' <see cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</see>
''' 例外が発生します。</remarks>
Public Function GetItemChecked(ByVal index As Integer) As Boolean
If (index < 0 OrElse index > Items.Count) Then
Throw New ArgumentOutOfRangeException("index", "範囲外の値が渡されました。")
Else
Return m_dropdown.List.GetItemChecked(index)
End If
End Function
''' <summary>
''' 指定したインデックスの項目のチェック状況を調べます。
''' </summary>
''' <param name="index">インデックス</param>
''' <returns></returns>
''' <remarks>index がリストの範囲外の場合、
''' <see cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</see>
''' 例外が発生します。</remarks>
Public Function GetItemCheckState(ByVal index As Integer) As CheckState
If (index < 0 OrElse index > Items.Count) Then
Throw New ArgumentOutOfRangeException("index", "範囲外の値が渡されました。")
Else
Return m_dropdown.List.GetItemCheckState(index)
End If
End Function
''' <summary>
''' 指定したインデックスの項目をチェックします。
''' </summary>
''' <param name="index">インデックス</param>
''' <param name="isChecked"></param>
''' <remarks>index がリストの範囲外の場合、
''' <see cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</see>
''' 例外が発生します。</remarks>
Public Sub SetItemChecked(ByVal index As Integer, ByVal isChecked As Boolean)
If (index < 0 OrElse index > Items.Count) Then
Throw New ArgumentOutOfRangeException("index", "範囲外の値が渡されました。")
Else
m_dropdown.List.SetItemChecked(index, isChecked)
' Text の更新に必要です
Me.Text = m_dropdown.GetCheckedItemsStringValue()
Me.SelectionLength = 0
End If
End Sub
''' <summary>
''' 指定したインデックスの項目のチェック状況を設定します。
''' </summary>
''' <param name="index">インデックス</param>
''' <param name="state"></param>
''' <remarks>index がリストの範囲外の場合、
''' <see cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</see>
''' 例外が発生します。</remarks>
Public Sub SetItemCheckState(ByVal index As Integer, ByVal state As CheckState)
If (index < 0 OrElse index > Items.Count) Then
Throw New ArgumentOutOfRangeException("index", "範囲外の値が渡されました。")
Else
m_dropdown.List.SetItemCheckState(index, state)
' Text の更新に必要です
Me.Text = m_dropdown.GetCheckedItemsStringValue()
Me.SelectionLength = 0
End If
End Sub
#End Region
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment