2

リストビューと一緒にコンボボックスの機能を使用できるオプションを探しています。

これは、出力を1つしか持てないSQLクエリがあるためです。これは、場合によっては取得できます。

たとえば、私のSQLテーブルは次のようになります

Unique_ID - Name
123456789 - Simon
987654321 - Simon

基本的に、同じ名前がデータベースに複数回存在する可能性があり、各エントリには独自のIDがあります。

ユーザーのために、IDに基づいて編集するレコードを選択させることはできません。代わりに、名前に基づいて選択したレコードを選択してもらいます。ただし、クエリの結果として複数のレコードがある場合、MySQL例外が発生します。

問題のMySQLクエリは次のとおりです。
"SELECT worldspace from survivor where is_dead = '0' _ and survivor.unique_id = (select unique_id from profile where name = '" & target & "')"

そのクエリからの出力は、別のUPDATEクエリで使用されます。

それで、私のコンボボックスがIDと名前の両方を値として持ち、それらの間に明確な分離があるので、リストビュー要素の場合のように、値全体が読みやすくなる可能性はありますか?

4

3 に答える 3

19

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.DrawStringTextRenderer.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本の線があるので、固定の高さで問題ありません。ItemHeight2行になるので、プロパティを通常の値の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と同じように動作します。

于 2013-03-20T05:29:39.850 に答える
6

これは、複数行のWPFComboBoxになります。

<ComboBox ItemsSource="{Binding}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding ID, StringFormat='{}ID: {0}'}"/>
                <TextBlock Text="{Binding Name, StringFormat='{}Name: {0}'}"/>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

データ項目:

public class ComboItem
{
    public int ID { get; set; }

    public string Name { get; set; }
}

結果:

ここに画像の説明を入力してください

すべてのハックフォームゾンビに:技術的な優位性はそれ自体で語っています。

于 2013-03-20T03:39:05.003 に答える
0

解決策は実際にははるかに簡単です。これを試して:

For i As Integer = 0 To dataSet.Tables(0).Rows.Count - 1

ComboBox1.Items.Add(dataSet.Tables(0).Rows(i)(0) + " | " + dataSet.Tables(0).Rows(i)(1) 
+ " | " + dataSet.Tables(0).Rows(i)(2))

Next

ただし、コードとDBに実装する必要があります

于 2020-09-21T12:19:11.630 に答える