ComboBoxOwnerDrawVariableフォントフォーマットサイズの問題

テリー

VisualStudioのGo Toメンバー検索に似たオートコンプリート/検索ボックスを実装しようとしています。

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

ただし、boldテキストの書式設定とその間隔が正しく計算されていません。これのオートコンプリート機能は省略し、検索語をハードコーディングして結果をフォーマットするコードのみを含めます。
によって決定された間隔e.Graphics.MeasureStringは正しい値を返さないようです。私が使用しようとしたStringFormat.GenericTypographicから、この質問、私は近いましたが、まだ正しくありません。

これは私のドロップダウンの表示であり、一致した用語(太字)は、フォーマット位置の計算がオフになっていることを簡単に示しています(f明らかにに侵入していますi)。

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

それに加えて、アイテムにカーソルを合わせると、太字なしでテキストが再描画されます。それもやめたいと思います。

更新:使用するようにコードを変更しましたTextRendererが、今ではさらに悪化しているように見えます。
私が連結する各試合の前後に余分なスペースがあるようです。

以下の更新されたコード:

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

private void Form1_Load( object sender, EventArgs e )
{
    var docGenFields = new[] {
        new DocGenFieldItem { Display = $"Profile.date-birth.value", Value = "5/9/1973", FieldCode = $"Profile.date-birth.value" },
        new DocGenFieldItem { Display = $"Profile.date-birth.text", Value = "Birth Date", FieldCode = $"Profile.date-birth.text" },
        new DocGenFieldItem { Display = $"Profile.date-birth.raw-value", Value = "1973-05-09", FieldCode = $"Profile.date-birth.raw-value" },
        new DocGenFieldItem { Display = $"Profile.name-first.value", Value = "Terry", FieldCode = $"Profile.name-first.value" },
        new DocGenFieldItem { Display = $"Profile.name-first.text", Value = "First Name", FieldCode = $"Profile.name-first.text" },
        new DocGenFieldItem { Display = $"Profile.name-first.raw-value", Value = "Terry", FieldCode = $"Profile.name-first.raw-value" },
        new DocGenFieldItem { Display = $"Profile.name-first.value", Value = "Minnesota", FieldCode = $"Profile.state.value" },
        new DocGenFieldItem { Display = $"Profile.name-first.text", Value = "State", FieldCode = $"Profile.state.text" },
        new DocGenFieldItem { Display = $"Profile.name-first.raw-value", Value = "MN", FieldCode = $"Profile.state.raw-value" }
    };

    comboBoxItems.FormattingEnabled = true;
    comboBoxItems.DrawMode = DrawMode.OwnerDrawVariable;
    comboBoxItems.DropDownHeight = 44 * 5;
    // comboBoxItems.Font = new Font( "Microsoft Sans Serif", 12F, FontStyle.Regular, GraphicsUnit.Point, 0 );
    comboBoxItems.Font = new Font( "Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 0 );
    comboBoxItems.Items.AddRange( docGenFields );

    comboBoxItems.DrawItem += new DrawItemEventHandler( comboBoxItems_DrawItem );
    comboBoxItems.MeasureItem += new MeasureItemEventHandler( comboBoxItems_MeasureItem );
}

private void comboBoxItems_DrawItem( object sender, DrawItemEventArgs e )
{
    // Draw the background of the item.
    e.DrawBackground();

    var listItem = comboBoxItems.Items[ e.Index ] as DocGenFieldItem;

    var searchTerm = "P";
    var matches = Regex.Split( listItem.Display, "(?i)" + searchTerm );

    var bold = new Font( e.Font.FontFamily, e.Font.Size, FontStyle.Bold );

    // e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

    var currentCharacter = 0;
    // float currentX = 0;
    var currentX = 0;
    var currentMatch = 0;
    var keyLength = searchTerm.Length;

    foreach ( var m in matches )
    {
        // If search term characters are first (like StartsWith) or last (like EndsWith) characters
        // then the match will be empty.  So if not empty, then need to render the characters 'between'
        // matches of search term in regular font
        if ( !string.IsNullOrEmpty( m ) )
        {
            // var p = new PointF( e.Bounds.X + currentX, e.Bounds.Y );
            // var mWidth = e.Graphics.MeasureString( m, e.Font, p, StringFormat.GenericTypographic );
            // e.Graphics.DrawString( m, e.Font, Brushes.Black, p );
            var p = new Point( currentX, e.Bounds.Y );
            var mWidth = TextRenderer.MeasureText( e.Graphics, m, e.Font );
            TextRenderer.DrawText( e.Graphics, m, e.Font, p, System.Drawing.Color.Black );
            currentX += mWidth.Width;
            currentCharacter += m.Length;
        }

        currentMatch++;

        // Render the search term characters (need to use 'substring' of current text to maintain
        // original case of text) *bold* in between matches.
        // string.IsNullOrEmpty( m ) && currentMatch == 1 - If the search term matches ENTIRE value
        // then currentMatch will = matches.Length (1) but the match of 'm' will be empty.
        if ( currentMatch < matches.Length || ( string.IsNullOrEmpty( m ) && currentMatch == 1 ) )
        {
            var mValue = listItem.Display.Substring( currentCharacter, keyLength );
                // var p = new PointF( e.Bounds.X + currentX, e.Bounds.Y );
            // var mWidth = e.Graphics.MeasureString( mValue, bold, p, StringFormat.GenericTypographic );
            // e.Graphics.DrawString( mValue, bold, Brushes.Black, p, StringFormat.GenericTypographic );

            var p = new Point( currentX, e.Bounds.Y );
            var mWidth = TextRenderer.MeasureText( e.Graphics, mValue, bold );
            TextRenderer.DrawText( e.Graphics, mValue, bold, p, System.Drawing.Color.Black );

            currentX += mWidth.Width;
            currentCharacter += keyLength;
        }
    }

    // Render a secondary 'info' line in the dropdown
    var b = new SolidBrush( ColorTranslator.FromHtml( "#636363" ) );
    var valueWidth = e.Graphics.MeasureString( "Value: ", bold );

    e.Graphics.DrawString( "Value: ", bold, b,
        new RectangleF( e.Bounds.X, e.Bounds.Y + 21, e.Bounds.Width, e.Bounds.Height )
    );
    e.Graphics.DrawString( listItem.Value, e.Font, b,
        new RectangleF( e.Bounds.X + valueWidth.Width, e.Bounds.Y + 21, e.Bounds.Width, 21 )
    );

    // Draw the focus rectangle if the mouse hovers over an item.
    e.DrawFocusRectangle();
}

private void comboBoxItems_MeasureItem( object sender, MeasureItemEventArgs e )
{
    e.ItemHeight = 44;
}
ジミ

When TextRenderer is used to render text in a non-generic Graphics context, this context needs to be considered: for this reason, TextRenderer provides overloads of both MeasureText and DrawText that accept a Graphics context (IDeviceContext) argument.
The Graphics context contains information that TextRenderer can use to better adapt to the DC specifics.

Also, we need to pass to the methods a combination of TextFormatFlags values that define how we want to measure and/or render the Text.

  • Always declare the type of Alignment
  • Specify the clipping/wrapping behavior (e.g., we want the Text to wrap or we really don't want it to, we want it clipped instead)
  • If the Text should NOT be padded, specify TextFormatFlags.NoPadding, otherwise the Text will be stretched to fill the drawing bounds.
  • If the the drawing bounds are not arranged manually (to draw text in specific positions), specify TextFormatFlags.LeftAndRightPadding to add a predefined padding to the text. The padding this setting applies (based on the Font kerning), matches the distance between the text and the borders of standard Controls (e.g., the ListBox or ListView)

More information about TextFormatFlags is (partially :) available in the Docs.

I've moved all the drawing parts to a single method, RenderText().
All measures and drawings are performed here: this way, it should be simpler to understand what is going on when the items are drawn.

The code in the DrawItem handler calls this method, passing some value that are proper when specific conditions are met (as changing the FontStyle, the alternative ForeColor of parts of the Text etc.)

Resulting in:

ComboBox OwnerDrawVariable TextRenderer

► The Font used here is Microsoft YaHei UI, 12pt. Of course you can use whatever other Font, but the System Font series with the UI appendix are designed (well) for this.

► Remember to dispose of the Graphics objects you create, it's very important, more important when theses objects are used to provide custom functionality to Controls, so probably constantly generated. Don't count on the Garbage Collector for this, it can do nothing for you in this context.

EDIT: Code optimization.

string searchTerm = string.Empty;
TextFormatFlags format = TextFormatFlags.Top | TextFormatFlags.Left | 
                         TextFormatFlags.NoClipping | TextFormatFlags.NoPadding;

private Size RenderText(string text, DrawItemEventArgs e, FontStyle style, Color altForeColor, Point offset)
{
    var color = altForeColor == Color.Empty ? e.ForeColor : altForeColor;
    using (var font = new Font(e.Font, style)) {
        var textSize = TextRenderer.MeasureText(e.Graphics, text, font, e.Bounds.Size, format);
        var rect = new Rectangle(offset, e.Bounds.Size);
        TextRenderer.DrawText(e.Graphics, text, font, rect, color, e.BackColor, format);
        return textSize;
    }
}

private IEnumerable<(string Text, bool Selected)> BuildDrawingString(string itemContent, string pattern)
{
    if (pattern.Length == 0) {
        yield return (itemContent, false);
    }
    else {
        var matches = Regex.Split(itemContent, $"(?i){pattern}");
        int pos = itemContent.IndexOf(pattern, StringComparison.CurrentCultureIgnoreCase);
        for (int i = 0; i < matches.Length; i++) {
            if (matches[i].Length == 0 && i < matches.Length - 1) {
                yield return (itemContent.Substring(pos, pattern.Length), matches[i].Length > 0 ? false : true);
            }
            else {
                yield return (matches[i], false);
                if (i < matches.Length - 1) {
                    yield return (itemContent.Substring(pos, pattern.Length), true);
                }
            }
        }
    }
}

private void comboBoxItems_DrawItem(object sender, DrawItemEventArgs e)
{
    var listItem = (sender as ComboBox).Items[e.Index] as DocGenFieldItem;
    e.DrawBackground();

    int drawingPosition = 0;
    foreach (var part in BuildDrawingString(listItem.Display, searchTerm)) {
        var style = part.Selected ? FontStyle.Bold : FontStyle.Regular;
        drawingPosition += RenderText(part.Text, e, style, Color.Empty, new Point(drawingPosition, e.Bounds.Y)).Width;
    }

    var offsetBottom = new Point(0, e.Bounds.Bottom - e.Font.Height - 2);
    var valueSize = RenderText("Value: ", e, FontStyle.Bold, Color.FromArgb(64, 64, 64), offsetBottom);

    offsetBottom.Offset(valueSize.Width, 0);
    RenderText(listItem.Value, e, FontStyle.Regular, Color.FromArgb(63, 63, 63), offsetBottom);
    e.DrawFocusRectangle();
}

private void comboBoxItems_MeasureItem(object sender, MeasureItemEventArgs e) 
    => e.ItemHeight = (sender as Control).Font.Height * 2 + 4;

更新前の質問で使用された方法Graphics.MeasureString()関連Graphics.DrawString()

  • 特定のStringFormatを使用してテキストを測定する場合、測定された境界を尊重するように描画する場合は、同じStringFormatを使用してテキストを描画します。
  • Graphics.TextRenderingHint = TextRenderingHint.AntiAlias代わりにGraphics.DrawString():useを使用してテキストをレンダリングすると、うまく機能しませんTextRenderingHint.ClearTypeGridFit
  • おそらく、Microsoft Sans Serifフォントとして避けるSegoe UI、使用するか、Microsoft YaHei UI代わりに(たとえば):これらのフォントははるかに適切に重み付けされ、このために明示的に設計されています(UI接尾辞はそれを与えます)。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

TOP 一覧

  1. 1

    STSでループプロセス「クラスパス通知の送信」のループを停止する方法

  2. 2

    Spring Boot Filter is not getting invoked if remove @component in fitler class

  3. 3

    Python / SciPyのピーク検出アルゴリズム

  4. 4

    セレンのモデルダイアログからテキストを抽出するにはどうすればよいですか?

  5. 5

    tkinterウィンドウを閉じてもPythonプログラムが終了しない

  6. 6

    androidsoongビルドシステムによるネイティブコードカバレッジ

  7. 7

    ZScalerと証明書の問題により、Dockerを使用できません

  8. 8

    VisualStudioコードの特異点/ドッカー画像でPythonインタープリターを使用するにはどうすればよいですか?

  9. 9

    ビュー用にサイズ変更した後の画像の高さと幅を取得する方法

  10. 10

    二次導関数を数値計算するときの大きな誤差

  11. 11

    Ansibleで複数行のシェルスクリプトを実行する方法

  12. 12

    画像変更コードを実行してもボタンの画像が変更されない

  13. 13

    Reactでclsxを使用する方法

  14. 14

    Three.js indexed BufferGeometry vs. InstancedBufferGeometry

  15. 15

    __init__。pyファイルの整理中に循環インポートエラーが発生しました

  16. 16

    PyTesseractを使用した背景色のため、スクリーンショットからテキストを読み取ることができません

  17. 17

    値間の一致を見つける最も簡単な方法は何ですか

  18. 18

    reCAPTCHA-エラーコード:ユーザーの応答を検証するときの「missing-input-response」、「missing-input-secret」(POSTの詳細がない)

  19. 19

    三項演算子良い練習の代わりとしてOptional.ofNullableを使用していますか?

  20. 20

    好き/愛の関係のためのデータベース設計

  21. 21

    エンティティIDを含む@RequestBody属性をSpringの対応するエンティティに変換します

ホットタグ

アーカイブ