2010年1月29日金曜日

.NET コンボボックスのオーナードロー ~カラーコンボボックス~

カラーコンボボックスのサンプルです。

コンボボックスのオーナードローはあちこちで解説されているので、そちらを参考にしてください。
DOBON.NETさん ComboBoxの項目を自分で描画する

今回参考にさせていただいたのはこちら。C#です。
Out Of Memoryさん オーナードローでデータバインドなコンボボックスのサンプル
DataSourceに設定したデータからDisplayMemberの値を取りだすところで、悩んでました。
たいへん参考になりました。ありがとうございます。


カラーコンボボックスのサンプルです。
Imports System.ComponentModel

Public Class ColorComboBox
    Inherits System.Windows.Forms.ComboBox


    Private _ColorMember As String

    Public Property ColorMember() As String
        Get
            Return Me._ColorMember
        End Get
        Set(ByVal value As String)
            Me._ColorMember = value
        End Set
    End Property


    Public Sub New()
        InitializeComponent()

        'アイテムの高さが均一の描画
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed

    End Sub


    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)

        '描画領域の取得
        Dim rctColor As New RectangleF(e.Bounds.Left, e.Bounds.Top, CSng(e.Bounds.Width * 0.3), e.Bounds.Height)
        Dim rctText As New RectangleF(rctColor.Right + CSng(e.Bounds.Width * 0.1), e.Bounds.Top, e.Bounds.Width, e.Bounds.Height)
        Dim rctTextBack As New RectangleF(rctColor.Right, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height)


        '文字列の描画
        If (e.Index <> -1) Then
            '--表示する文字列の取得 
            Dim objTxt As Object = Me.Items(e.Index)
            If (Me.DataSource IsNot Nothing) Then
                Dim properties As PropertyDescriptorCollection = Me.BindingContext(Me.DataSource).GetItemProperties()
                Dim prpDisplayMember As PropertyDescriptor = properties(Me.DisplayMember)
                If (prpDisplayMember IsNot Nothing) Then
                    objTxt = prpDisplayMember.GetValue(Me.Items(e.Index))
                End If
            End If
            Dim txt As String = System.ComponentModel.TypeDescriptor.GetConverter(objTxt).ConvertToString(objTxt)

            '--表示する文字列の書式
            Dim sf As StringFormat = CType(StringFormat.GenericDefault.Clone, StringFormat)
            sf.Alignment = StringAlignment.Near
            sf.LineAlignment = StringAlignment.Center
            '--文字列領域の前景色と背景色の取得
            Dim clrTxtBack As Color
            Dim clrTxtFore As Color
            If ((e.State And DrawItemState.Disabled) <> 0) Then
                clrTxtBack = SystemColors.Window
                clrTxtFore = SystemColors.GrayText
            ElseIf (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                If (e.State And DrawItemState.ComboBoxEdit) = DrawItemState.ComboBoxEdit Then
                    clrTxtBack = SystemColors.Window
                    clrTxtFore = Me.ForeColor
                Else
                    clrTxtBack = SystemColors.Highlight
                    clrTxtFore = SystemColors.HighlightText
                End If
            Else
                clrTxtBack = SystemColors.Window
                clrTxtFore = Me.ForeColor
            End If
            '--文字列領域の描画
            Using brush As New SolidBrush(clrTxtBack)
                e.Graphics.FillRectangle(brush, rctTextBack)
            End Using
            Using brush As New SolidBrush(clrTxtFore)
                e.Graphics.DrawString(txt, e.Font, brush, rctText, sf)
            End Using

            'カラーボックスの描画
            '--表示するカラーの取得
            Dim objClr As Object = Me.Items(e.Index)
            If (Me.DataSource IsNot Nothing) Then
                Dim properties As PropertyDescriptorCollection = Me.BindingContext(Me.DataSource).GetItemProperties()
                Dim prpColorMember As PropertyDescriptor = properties(Me.ColorMember)
                If (prpColorMember IsNot Nothing) Then
                    objClr = prpColorMember.GetValue(Me.Items(e.Index))
                End If
            End If
            Dim clr As Color = CType(objClr, System.Drawing.Color)
            Using backBrush As New SolidBrush(clr)
                e.Graphics.FillRectangle(backBrush, rctColor)
            End Using

        End If

        ''Forcus枠の描画
        'If ((e.State And DrawItemState.Focus) <> 0) Then
        '    e.DrawFocusRectangle()
        'End If

    End Sub


End Class

テストコードです。
フォームにカラーコンボボックスを2つ配置し以下のコードを実行します。
Public Class Form1


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim props As System.Reflection.PropertyInfo() = GetType(Color).GetProperties(System.Reflection.BindingFlags.Public Or System.Reflection.BindingFlags.Static)

        'ItemsプロパティにColorオブジェクトを追加
        Dim items As New List(Of Object)
        For Each prop As System.Reflection.PropertyInfo In props
            If (prop.PropertyType IsNot GetType(Color)) Then
                Continue For
            End If
            Dim color As Color = CType(prop.GetValue(Nothing, Nothing), Color)
            If (color = color.Transparent) Then
                Continue For
            Else
                items.Add(color)
            End If
        Next
        Me.ColorComboBox1.Items.AddRange(items.ToArray)

        'DataSourceにDataTableを指定
        Dim dt As New DataTable()
        dt.Columns.Add("Cd", GetType(Integer))
        dt.Columns.Add("Color", GetType(Color))
        dt.Columns.Add("Name", GetType(String))
        Dim cd As Integer = 1
        For Each prop As System.Reflection.PropertyInfo In props
            If (prop.PropertyType IsNot GetType(Color)) Then
                Continue For
            End If
            Dim color As Color = CType(prop.GetValue(Nothing, Nothing), Color)
            If (color = color.Transparent) Then
                Continue For
            Else
                dt.Rows.Add(cd, color, color.Name)
                cd += 1
            End If
        Next

        Me.ColorComboBox2.ValueMember = "Cd"
        Me.ColorComboBox2.DisplayMember = "Name"
        Me.ColorComboBox2.ColorMember = "Color"
        Me.ColorComboBox2.DataSource = dt


    End Sub
End Class

0 件のコメント: