DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

ComboBoxの項目を自分で描画する

コンボボックスをオーナードローする方法を紹介します。

まず、オーナードローしたいコンボボックスのDrawModeプロパティをDrawMode.OwnerDrawFixedまたはDrawMode.OwnerDrawVariableにします。OwnerDrawFixedの場合、項目の高さを個別に変更することができません(つまり、すべての項目が同じ高さとなります)。OwnerDrawVariableを指定した時は、MeasureItemイベントハンドラで項目の高さを個別に指定できます。MeasureItemイベントが発生するのは、DrawModeプロパティがOwnerDrawVariableに指定されている時のみです。

項目の描画はDrawItemイベントハンドラで行います。e.Graphicsで得られるGraphicsオブジェクトに対して具体的な描画を行います。

次に具体的な例を示します。この例では、コンボボックスComboBox1の項目にフォント名のリストを追加し、オーナードローにより、そのフォントで項目のテキストを描画するようにしています。なお、インストールされているフォントを取得する方法はこちらで説明しています。

VB.NET
コードを隠すコードを選択
Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
    'オーナードローを指定
    'DrawMode.OwnerDrawVariableを指定した時は、
    'MeasureItemが発生する
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed
    '項目の高さを設定
    ComboBox1.ItemHeight = 20
    'DrawItemイベントハンドラの追加
    AddHandler ComboBox1.DrawItem, AddressOf ComboBox1_DrawItem

    'フォント名をComboBox1のリストに追加する
    'InstalledFontCollectionオブジェクトの取得
    Dim ifc As New System.Drawing.Text.InstalledFontCollection
    'インストールされているすべてのフォントファミリアを取得
    Dim ffs As FontFamily() = ifc.Families
    Dim ff As FontFamily
    For Each ff In ffs
        'ここではスタイルにRegularが使用できるフォントのみを表示
        If ff.IsStyleAvailable(FontStyle.Regular) Then
            ComboBox1.Items.Add(ff.Name)
        End If
    Next ff
End Sub

'DrawItemイベントハンドラ
'項目を描画する
Private Sub ComboBox1_DrawItem(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DrawItemEventArgs)
    '背景を描画する
    '項目が選択されている時は強調表示される
    e.DrawBackground()

    Dim cmb As ComboBox = CType(sender, ComboBox)
    '項目に表示する文字列
    Dim txt As String
    If e.Index > -1 Then
        txt = cmb.Items(e.Index).ToString()
    Else
        txt = cmb.Text
    End If
    '使用するフォント
    Dim f As New Font(txt, cmb.Font.Size)
    '使用するブラシ
    Dim b = New SolidBrush(e.ForeColor)
    '文字列を描画する
    Dim ym As Single = _
        (e.Bounds.Height - e.Graphics.MeasureString(txt, f).Height) / 2
    e.Graphics.DrawString(txt, f, b, e.Bounds.X, e.Bounds.Y + ym)

    f.Dispose()
    b.Dispose()

    'フォーカスを示す四角形を描画
    e.DrawFocusRectangle()
End Sub
C#
コードを隠すコードを選択
private void Form1_Load(object sender, System.EventArgs e)
{
    ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
    //オーナードローを指定
    //DrawMode.OwnerDrawVariableを指定した時は、
    //MeasureItemが発生する
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed;
    //項目の高さを設定
    ComboBox1.ItemHeight = 20;
    //DrawItemイベントハンドラの追加
    ComboBox1.DrawItem += new DrawItemEventHandler(ComboBox1_DrawItem);

    //フォント名をComboBox1のリストに追加する
    //InstalledFontCollectionオブジェクトの取得
    System.Drawing.Text.InstalledFontCollection ifc = 
        new System.Drawing.Text.InstalledFontCollection();
    //インストールされているすべてのフォントファミリアを取得
    FontFamily[] ffs = ifc.Families;
    foreach (FontFamily ff in ffs)
        //ここではスタイルにRegularが使用できるフォントのみを表示
        if (ff.IsStyleAvailable(FontStyle.Regular))
            ComboBox1.Items.Add(ff.Name);
}

//DrawItemイベントハンドラ
//項目を描画する
private void ComboBox1_DrawItem(object sender, 
    System.Windows.Forms.DrawItemEventArgs e)
{
    //背景を描画する
    //項目が選択されている時は強調表示される
    e.DrawBackground();

    ComboBox cmb = (ComboBox) sender;
    //項目に表示する文字列
    string txt = e.Index > -1 ? cmb.Items[e.Index].ToString() : cmb.Text;
    //使用するフォント
    Font f = new Font(txt, cmb.Font.Size);
    //使用するブラシ
    Brush b = new SolidBrush(e.ForeColor);
    //文字列を描画する
    float ym = 
        (e.Bounds.Height - e.Graphics.MeasureString(txt, f).Height) / 2;
    e.Graphics.DrawString(txt, f, b, e.Bounds.X, e.Bounds.Y + ym);

    f.Dispose();
    b.Dispose();
    
    //フォーカスを示す四角形を描画
    e.DrawFocusRectangle();
}

上記の例ではOwnerDrawFixedとしていますが、フォントにより項目の高さが異なるため、本来ならばOwnerDrawVariableを使いたいところです。ところが、OwnerDrawVariableによりオーナードローしたコンボボックスは、項目のリストをページ単位で下にスクロールさせた時、なぜか上にスクロールしたような見え方になります。OwnerDrawFixedではこのような現象が起こりません。(.NET Framework 1.1、2.0で確認)

DrawModeをOwnerDrawVariableとした例も紹介しましょう。OwnerDrawVariableとした場合は、ComboBoxのMeasureItemイベントで項目のサイズを指定します。上記コードとの変更点は、DrawModeをOwnerDrawVariableとしたのと、ComboBox1のMeasureItemイベントハンドラを追加した点のみです。

VB.NET
コードを隠すコードを選択
Private Sub Form1_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Load
    ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
    'オーナードローを指定
    'DrawMode.OwnerDrawVariableを指定した時は、
    'MeasureItemが発生する
    ComboBox1.DrawMode = DrawMode.OwnerDrawVariable
    '項目の高さを設定
    ComboBox1.ItemHeight = 20
    'DrawItemイベントハンドラの追加
    AddHandler ComboBox1.DrawItem, AddressOf ComboBox1_DrawItem
    AddHandler ComboBox1.MeasureItem, AddressOf ComboBox1_MeasureItem

    'フォント名をComboBox1のリストに追加する
    'InstalledFontCollectionオブジェクトの取得
    Dim ifc As New System.Drawing.Text.InstalledFontCollection
    'インストールされているすべてのフォントファミリアを取得
    Dim ffs As FontFamily() = ifc.Families
    Dim ff As FontFamily
    For Each ff In ffs
        'ここではスタイルにRegularが使用できるフォントのみを表示
        If ff.IsStyleAvailable(FontStyle.Regular) Then
            ComboBox1.Items.Add(ff.Name)
        End If
    Next ff
End Sub

'DrawItemイベントハンドラ
'項目を描画する
Private Sub ComboBox1_DrawItem(ByVal sender As Object, _
  ByVal e As System.Windows.Forms.DrawItemEventArgs)
    '背景を描画する
    '項目が選択されている時は強調表示される
    e.DrawBackground()

    Dim cmb As ComboBox = CType(sender, ComboBox)
    '項目に表示する文字列
    Dim txt As String
    If e.Index > -1 Then
        txt = cmb.Items(e.Index).ToString()
    Else
        txt = cmb.Text
    End If
    '使用するフォント
    Dim f As New Font(txt, cmb.Font.Size)
    '使用するブラシ
    Dim b = New SolidBrush(e.ForeColor)
    '文字列を描画する
    Dim ym As Single = _
     (e.Bounds.Height - e.Graphics.MeasureString(txt, f).Height) / 2
    e.Graphics.DrawString(txt, f, b, e.Bounds.X, e.Bounds.Y + ym)

    f.Dispose()
    b.Dispose()

    'フォーカスを示す四角形を描画
    e.DrawFocusRectangle()
End Sub

'ComboBox1のMeasureItemイベントハンドラ
Private Sub ComboBox1_MeasureItem(ByVal sender As Object, _
  ByVal e As MeasureItemEventArgs)
    Dim cmb As ComboBox = CType(sender, ComboBox)
    Dim txt As String
    If e.Index > -1 Then
        txt = cmb.Items(e.Index).ToString()
    Else
        txt = cmb.Text
    End If
    '使用するフォント
    Dim f As New Font(txt, cmb.Font.Size)
    '項目の高さを決定
    e.ItemHeight = CInt(e.Graphics.MeasureString(txt, f).Height)
End Sub
C#
コードを隠すコードを選択
private void Form1_Load(object sender, System.EventArgs e)
{
    ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
    //オーナードローを指定
    //DrawMode.OwnerDrawVariableを指定した時は、
    //MeasureItemが発生する
    ComboBox1.DrawMode = DrawMode.OwnerDrawVariable;
    //項目の高さを設定
    ComboBox1.ItemHeight = 20;
    //DrawItemイベントハンドラの追加
    ComboBox1.DrawItem += new DrawItemEventHandler(ComboBox1_DrawItem);

    //フォント名をComboBox1のリストに追加する
    //InstalledFontCollectionオブジェクトの取得
    System.Drawing.Text.InstalledFontCollection ifc =
        new System.Drawing.Text.InstalledFontCollection();
    //インストールされているすべてのフォントファミリアを取得
    FontFamily[] ffs = ifc.Families;
    foreach (FontFamily ff in ffs)
        //ここではスタイルにRegularが使用できるフォントのみを表示
        if (ff.IsStyleAvailable(FontStyle.Regular))
            ComboBox1.Items.Add(ff.Name);
}

//ComboBox1のDrawItemイベントハンドラ
//項目を描画する
private void ComboBox1_DrawItem(object sender,
    System.Windows.Forms.DrawItemEventArgs e)
{
    //背景を描画する
    //項目が選択されている時は強調表示される
    e.DrawBackground();

    ComboBox cmb = (ComboBox)sender;
    //項目に表示する文字列
    string txt = e.Index > -1 ? cmb.Items[e.Index].ToString() : cmb.Text;
    //使用するフォント
    Font f = new Font(txt, cmb.Font.Size);
    //使用するブラシ
    Brush b = new SolidBrush(e.ForeColor);
    //文字列を描画する
    float ym =
        (e.Bounds.Height - e.Graphics.MeasureString(txt, f).Height) / 2;
    e.Graphics.DrawString(txt, f, b, e.Bounds.X, e.Bounds.Y + ym);

    f.Dispose();
    b.Dispose();

    //フォーカスを示す四角形を描画
    e.DrawFocusRectangle();
}

//ComboBox1のMeasureItemイベントハンドラ
private void ComboBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
    ComboBox cmb = (ComboBox)sender;
    //項目に表示する文字列
    string txt = e.Index > -1 ? cmb.Items[e.Index].ToString() : cmb.Text;
    //使用するフォント
    Font f = new Font(txt, cmb.Font.Size);
    //項目の高さを決定
    e.ItemHeight = (int)e.Graphics.MeasureString(txt, f).Height;
}
  • 履歴:
  • 2006/11/12 DrawModeをOwnerDrawVariableとした例を追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。