WPFのすべてがどれほど簡単で、WinFormsがどれだけ吸うかについて、すでにHighCoreの扱いを受けているようです。ただし、WinFormsでもこれを実行できることを知りたいと思うかもしれません。あなたはそれを少し違ったやり方でします。WinFormsとWPFで標準の設計イディオムが異なるのは当然のことです。これは、一方が他方よりも「優れている」ことを正当化するものではなく、使用しているものの使用方法を学ぶ必要があることを意味します。(確かに、20年前にWindows自体で発明されたUIフレームワークを使用して実現するのが少し難しいものもあります。そのパワーはかなり注目に値します。)
情報をフォーマットする基本的な方法は2つあります。1行のすべて(質問で求めたものだと思います)、または各項目が基本的に2行の単位である2行の情報(これはHighCoreのWPFソリューションが示しています)。
単一行フォーマット
単純なアプローチ
最初にすべてを1行に配置することを検討しますが、これは非常に簡単です。|
区切りに列は必要ありません。質問で使用したような垂直パイプ( )やダッシュ( )など、アイテムをコンボボックスに追加するときに、ある種の特徴的な区切り文字を使用でき-
ます。
ComboBox.Items.Add
これは、メソッドがタイプのパラメーターを受け入れるため、非常にうまく機能します。このパラメーターをObject
呼び出すだけToString
で、コントロールに表示される値を取得できます。文字列を渡すと、その文字列が表示されます。
myComboBox.BeginUpdate()
For Each record In myRecordSet
myComboBox.Items.Add(String.Format("{0} | {1}", record.UniqueID, record.Name))
' or even...
myComboBox.Items.Add(String.Format("{0} ({1})", record.UniqueID, record.Name))
Next record
myComboBox.EndUpdate()
また 
OOPによる段階的な改善
Add
一意のIDと名前のプロパティ(およびその他の必要なもの)を追跡し、ToString
表示目的でメソッドをオーバーライドするカスタムクラスをメソッドに渡すこともできます。
Public Class Record
Public Property UniqueID As Long ' maybe this should be a string too
Public Property Name As String
Public Overrides Function ToString() As String
' Generate the string that will be displayed in the combobox for this
' record, just like we did above when adding it directly to the combobox,
' except that in this case, it will be dynamically generated on the fly,
' allowing you to also track state information along with each item.
Return String.Format("{0} | {1}", Me.UniqueID, Me.Name)
End Function
End Class
' ...
' (somewhere else, when you add the items to the combobox:)
myComboBox.BeginUpdate()
For Each r In myRecordSet
' Create a Record object representing this item, and set its properties.
Dim newRecord As New Record
newRecord.UniqueID = r.UniqueID
newRecord.Name = r.Name
' ...etc.
' Then, add that object to the combobox.
myComboBox.Items.Add(newRecord)
Next r
myComboBox.EndUpdate()
ジャギーの修正
確かに、各セットの最初の項目が可変長であり、可変幅フォント(つまり、コードエディタを除く地球上のすべてのUIのように等幅フォントではないフォント)を使用している場合、セパレータは整列しません上に行くと、2つの適切にフォーマットされた列を取得できません。代わりに、それはすべてごちゃごちゃして醜いように見えます。

ComboBoxコントロールでサポートされているタブ文字があれば、すべてを自動的に並べることができますが、残念ながらそうではありません。これは、残念ながら、基盤となるWin32コントロールの厳しい制限です。
この不規則なエッジの問題を修正することは可能ですが、少し複雑になります。コンボボックス内のアイテムの描画を引き継ぐ必要があります。これは「所有者描画」と呼ばれます。
これを行うには、DrawMode
プロパティをに設定しOwnerDrawFixed
、イベントを処理しDrawItem
て手動でテキストを描画します。このメソッドを使用してTextRenderer.DrawText
、キャプション文字列を描画し(WinFormsが内部で使用するものと一致するため、使用を避けますGraphics.DrawString
)、TextRenderer.MeasureText
必要に応じて間隔を正しくします。DrawItemEventArgs
描画コードは、渡されたとして提供されるデフォルトのプロパティをすべて使用できます(使用する必要があります)e
。この場合、各アイテムの幅と高さを変更できないため、OwnerDrawVariable
モードやイベントの処理は必要ありません。MeasureItem
アイデアを与えるために、ドロップダウンを垂直方向に半分に分割するだけの手っ取り早い実装を次に示します。
Private Sub myComboBox_DrawItem(sender As Object, e As DrawItemEventArgs) Handles myComboBox.DrawItem
' Fill the background.
e.DrawBackground()
' Extract the Record object corresponding to the combobox item to be drawn.
Dim record As Record = DirectCast(myComboBox.Items(e.Index), Record)
Dim id As String = record.UniqueID.ToString()
Dim name As String = record.Name
' Calculate important positions based on the area of the drop-down box.
Dim xLeft As Integer = e.Bounds.Location.X
Dim xRight As Integer = xLeft + e.Bounds.Width
Dim xMid As Integer = (xRight - xLeft) / 2
Dim yTop As Integer = e.Bounds.Location.Y
Dim yBottom As Integer = yTop + e.Bounds.Height
' Draw the first (Unique ID) string in the first half.
TextRenderer.DrawText(e.Graphics, id, e.Font, New Point(xLeft, yTop), e.ForeColor)
' Draw the column separator line right down the middle.
e.Graphics.DrawLine(SystemPens.ButtonFace, xMid, yTop, xMid, yBottom)
' Draw the second (Name) string in the second half, adding a bit of padding.
TextRenderer.DrawText(e.Graphics, name, e.Font, New Point(xMid + 5, yTop), e.ForeColor, TextFormatFlags.Left)
' Finally, draw the focus rectangle.
e.DrawFocusRectangle()
End Sub

今、これはかなりよく見えます。イベントハンドラーメソッドで使用される手法を確実に改善できDrawItem
ますが、コンボボックスが表示される値に対して適切なサイズになっている限り、それは問題なく機能します。
複数行フォーマット
カスタムComboBoxクラスの定義
各項目がHighCoreのWPFの例のように2行のグループである、2番目の方法は、組み込みのComboBoxコントロールをサブクラス化し、その描画ルーチンを完全に制御することによって最もよく実行されます。しかし、それは何も恐れることはありません。コントロールをサブクラス化することは、UIをさらに制御するための標準的なWinFormsイディオムです。(もちろん、上記のようにイベントを処理することでこれらすべてを実装できますが、サブクラス化ははるかにクリーンなアプローチであり、すべてが同じように動作する複数のコンボボックスが必要な場合は再利用も促進すると思います。)
OwnerDrawVariable
繰り返しになりますが、アイテムの高さは変わらないので、必要ありません。常に2本の線があるので、固定の高さで問題ありません。ItemHeight
2行になるので、プロパティを通常の値の2倍に設定する必要があります。を使用してこれを複雑な方法TextRenderer.MeasureText
で行うことも、デフォルト値に2を掛けるだけで簡単な方法で行うこともできます。このデモでは後者を選択しました。
このクラスをプロジェクトに追加してMultiLineComboBox
から、組み込みの代わりにコントロールを使用しSystem.Windows.Forms.ComboBox
ます。すべてのプロパティとメソッドは同じように機能します。
Public Class MultiLineComboBox : Inherits ComboBox
Public Sub New()
' Call the base class.
MyBase.New()
' Typing a value into this combobox won't make sense, so make it impossible.
Me.DropDownStyle = ComboBoxStyle.DropDownList
' Set the height of each item to be twice its normal value
' (because we have two lines instead of one).
Me.ItemHeight *= 2
End Sub
Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
' Call the base class.
MyBase.OnDrawItem(e)
' Fill the background.
e.DrawBackground()
' Extract the Record object corresponding to the combobox item to be drawn.
If (e.Index >= 0) Then
Dim record As Record = DirectCast(Me.Items(e.Index), Record)
' Format the item's caption string.
Dim caption As String = String.Format("ID: {0}{1}Name: {2}", record.UniqueID.ToString(), Environment.NewLine, record.Name)
' And then draw that string, left-aligned and vertically centered.
TextRenderer.DrawText(e.Graphics, caption, e.Font, e.Bounds, e.ForeColor, TextFormatFlags.Left Or TextFormatFlags.VerticalCenter)
End If
' Finally, draw the focus rectangle.
e.DrawFocusRectangle()
End Sub
End Class

ファンシーと繁栄を追加する
私たちが今持っているものは悪くはありませんが、でコードを描くことにもう少し努力を惜しまないことによってOnDrawItem
、私たちはいくつかの視覚的な空想と繁栄を追加することができます。
たとえば、選択長方形がないと、これらが実際には2行の単位であると判断するのはかなり困難です。これはコンボボックスコントロールでは珍しいことです。したがって、使いやすさの理由から、アプリケーションはこれを十分に明確にするために邪魔にならないようにする必要があります。これを行う1つの方法は、2行目をインデントすることです。組み込みのコンボボックスコントロールはタブをサポートしていないと言ったことを思い出してください。今は自分で絵を描いているので、それはもう当てはまりません。2行目の先頭にパディングを追加することで、タブをエミュレートできます。
ラベル(「ID:」と「Name:」)を実際の値とは別に設定したい場合は、それも可能です。おそらく、ラベルを太字にし、テキストの色を明るくします。
つまり、描画コードを操作するだけで、ほぼすべての効果を作成できることがわかります。私たちは完全に制御MultiLineComboBox
でき、どこでも再利用できるクラスにすべてをまとめることで、コードの残りの部分は、何か特別なことが起こっていることを知る必要さえありません。かっこいいですよね?
すべての作業を回避する
そして最後に、この作業をすべてスキップして、すでに作成されているさまざまなカスタムマルチラインコンボボックスコントロールを選択できることを指摘しなかった場合は、残念に思います。
これはかなり気の利いたものです。コンボボックスのドロップダウン矢印をクリックすると、実際にはListViewコントロールが表示されます。そうすることで、ListViewコントロールのすべての書式設定機能を無料で利用できますが、通常のComboBoxと同じように動作します。