ListView子项OwnerDraw文本的粗体部分 [英] ListView SubItem OwnerDraw Bold Part of the Text

查看:94
本文介绍了ListView子项OwnerDraw文本的粗体部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了结束我的所有者绘制C#控件的历险,我的最后一个问题是在ListView中.我有一个ListView,它将始终处于详细信息"视图模式下,具有两列-名称和值.对于名称列,我将始终使用DefaultDraw,对于值"列,我想将搜索词的所有匹配项加粗.

To finish off my adventure of owner drawing C# controls, my last problem is in a ListView. I have a ListView that will always be in the Detail view mode with two columns - Name and Value. For the Name column, I'll always use DefaultDraw, and for the Value column, I want to bold every match of a search term.

我的 TreeView问题和我有关所有者绘图的 ComboBox问题一样,到目前为止,这些代码已经成型.我不需要任何背景或边框绘制,因此它比我所见过的其他一些问题更简单,但是到目前为止,我仍然注意到我的问题.

My TreeView question and my ComboBox question about owner drawing helped shape the code so far. I don't need any background or border drawing so it is simpler than some of the other questions I've seen people ask, but I have problem that I'm noticing so far.

当我缩小一列并期望...时,如果我渲染了多个字符串部分(由于搜索词匹配),则每个字符串都有自己的...,具体取决于它长于列的宽度.如果不存在搜索项,并且我将e.SubItem.Text渲染为一个普通字符串,则其行为将与预期的一样.

When I shrink a column and expect a ..., if I've rendered multiple string parts (because of a match from the search term), each string has its own ... based on when it is longer than the column's width. If there is no presence of the search term and I render the e.SubItem.Text as one normal string, it behaves as expected.

在以下系列中,地址1 是完整的字符串.其他两个项目的字符串为 110 M aple Avenue .

In the series below, Address 1 is a complete string. The other two items have strings of 110 M, aple, and Avenue.

相对于...,是否有办法使整个字符串充当单个实体?在简短的测试中,我还没有发现任何其他问题,但是很高兴在需要时提出一些建议.

Is there a way to make the entire string act as a single entity with regards to ...? In my brief testing, I haven't found any other issues yet, but happy to take some advice if needed.

调整大小之前

首次调整大小-仅地址1显示...

First Resize - Only Address 1 shows ...

第二次调整大小-大道秀...

Second Resize - Avenue Shows ...

第三次调整大小-110 M显示...

Third Resize - 110 M Shows ...

最终调整大小-每个字符串"均显示...

Final Resize - Every 'string' Shows ...

    TextFormatFlags subItemFlags = TextFormatFlags.Left | TextFormatFlags.Bottom | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPadding;

    private void InitializeListView()
    {
        listView.OwnerDraw = true;
        listView.DrawColumnHeader += listView_DrawColumnHeader;
        listView.DrawSubItem += listView_DrawSubItem;
        listView.Font = new Font( "Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 0 );
    }

    private void listView_DrawColumnHeader( object sender, DrawListViewColumnHeaderEventArgs e ) => e.DrawDefault = true;
    private void listView_DrawSubItem( object sender, DrawListViewSubItemEventArgs e )
    {
        if ( e.ColumnIndex == 0)
        {
            e.DrawDefault = true;
        }
        else
        {
            var textPadding = 2;

            using ( var boldFont = new Font( listView.Font, FontStyle.Bold ) )
            {
                var stringParts = BuildDrawingString( e.SubItem.Text, e.Graphics, e.Bounds.Size, fieldSearch.Text, listView.Font, boldFont, subItemFlags ).ToArray();
                var color = listView.ForeColor;
                var point = new Point( e.SubItem.Bounds.X + textPadding, e.SubItem.Bounds.Y );

                foreach ( var part in stringParts )
                {
                    var font = part.Selected ? boldFont : listView.Font;
                    DrawText( part.Text, e.Graphics, e.SubItem.Bounds.Size, font, point, color, e.SubItem.BackColor, subItemFlags );
                    point.Offset( part.Width, 0 );
                }
            }
        }
    }
    private void DrawText( string text, Graphics graphics, Size size, Font font, Point offset, Color foreColor, Color backColor, TextFormatFlags formatFlags )
    {
        var rect = new Rectangle( offset, size );
        TextRenderer.DrawText( graphics, text, font, rect, foreColor, backColor, formatFlags );
    }

    private IEnumerable<(string Text, bool Selected, int Width)> BuildDrawingString( string textToRender, Graphics graphics, Size proposedSize, string pattern, Font normalFont, Font boldFont, TextFormatFlags formatFlags )
    {
        int measureText( string t, bool s ) => TextRenderer.MeasureText( graphics, t, s ? boldFont : normalFont, proposedSize, formatFlags ).Width;

        if ( pattern.Length == 0 )
        {
            yield return (textToRender, false, measureText( textToRender, false ));
        }
        else
        {
            var matches = Regex.Split( textToRender, $"(?i){pattern}" );
            var currentCharacter = 0;
            var patternLength = pattern.Length;

            for ( int i = 0; i < matches.Length; i++ )
            {
                if ( matches[ i ].Length >= 0 )
                {
                    yield return (
                        matches[ i ], 
                        false, 
                        measureText( matches[ i ], false ) 
                    );

                    currentCharacter += matches[ i ].Length;
                }

                if ( i < matches.Length - 1 )
                {
                    var matchText = textToRender.Substring( currentCharacter, patternLength );
                    yield return (
                        matchText,
                        true,
                        measureText( matchText, true )
                    );

                    currentCharacter += patternLength;
                }
            }
        }
    }

推荐答案

因此,在对此进行了一些思考之后,我意识到每个文本部分(粗体和非粗体)都是针对整个子项"进行测量的. .相反,我必须继续减小TextRenderer.MeasureText的允许大小,以便它知道何时放入...,然后在使用...渲染任何项目后立即停止处理其他字符串.因此,我的BuildDrawingsString必须考虑到这一点,并且还必须为每个零件返回一个AllowedWidth,以便对TextRenderer.DrawText的实际调用也将每次都使用正确的Size.

So after thinking about this a bit, I realized that each text part (bold and non-bold) were being measured against the 'entire subitem' Bounds.Size. I had to instead keep decreasing the allowed size for TextRenderer.MeasureText so it knew when to put in the ... and then immediately stop processing additional strings as soon as any item was rendered with .... So my BuildDrawingsString had to account for this, and it also had to return an AllowedWidth for each part as well, so that the actual call to TextRenderer.DrawText would use the correct Size each time as well.

    private void listView_DrawSubItem( object sender, DrawListViewSubItemEventArgs e )
    {
        if ( e.ColumnIndex == 0)
        {
            e.DrawDefault = true;
        }
        else
        {
            var textPadding = 2;

            using ( var boldFont = new Font( listView.Font, FontStyle.Bold ) )
            {
                var stringParts = BuildDrawingString( e.SubItem.Text, e.Graphics, e.SubItem.Bounds.Size, fieldSearch.Text, listView.Font, boldFont, subItemFlags, true );
                var color = listView.ForeColor;
                var point = new Point( e.SubItem.Bounds.X + textPadding, e.SubItem.Bounds.Y );

                foreach ( var part in stringParts )
                {
                    var font = part.Selected ? boldFont : listView.Font;
                    // System.Diagnostics.Trace.WriteLine( e.SubItem.Bounds.Size + ", " + part.Width );
                    DrawText( part.Text, e.Graphics, new Size( part.AllowedWidth, e.SubItem.Bounds.Size.Height ), font, point, color, e.SubItem.BackColor, subItemFlags );
                    point.Offset( part.Width, 0 );
                }
            }
        }
    }

    private IEnumerable<(string Text, bool Selected, int Width, int AllowedWidth)> BuildDrawingString( string textToRender, Graphics graphics, Size proposedSize, string pattern, Font normalFont, Font boldFont, TextFormatFlags formatFlags, bool isListView )
    {
        var totalWidth = 0;
        (int width, int allowedWidth, bool isTruncated) measureText( string t, bool s )
        {
            var size = new Size( proposedSize.Width - totalWidth, proposedSize.Height );
            var width = TextRenderer.MeasureText( graphics, t, s ? boldFont : normalFont, size, formatFlags ).Width;
            var truncated = isListView && TextRenderer.MeasureText( graphics, t, s ? boldFont : normalFont, size, formatFlags & ~TextFormatFlags.EndEllipsis ).Width != width;

            return ( width, size.Width, truncated );
        }

        if ( pattern.Length == 0 )
        {
            yield return ( textToRender, false, measureText( textToRender, false ).width, proposedSize.Width );
        }
        else
        {
            var matches = Regex.Split( textToRender, $"(?i){pattern}" );
            var currentCharacter = 0;
            var patternLength = pattern.Length;

            for ( int i = 0; i < matches.Length; i++ )
            {
                if ( matches[ i ].Length >= 0 )
                {
                    var measureInfo = measureText( matches[ i ], false );
                    totalWidth += measureInfo.width;

                    yield return (
                        matches[ i ],
                        false,
                        measureInfo.width,
                        measureInfo.allowedWidth
                    );

                    currentCharacter += matches[ i ].Length;

                    if ( measureInfo.isTruncated )
                    {
                        yield break;
                    }
                }

                if ( i < matches.Length - 1 )
                {
                    var matchText = textToRender.Substring( currentCharacter, patternLength );
                    var measureInfo = measureText( matchText, true );
                    totalWidth += measureInfo.width;

                    yield return (
                        matchText,
                        true,
                        measureInfo.width,
                        measureInfo.allowedWidth
                    );

                    currentCharacter += patternLength;

                    if ( measureInfo.isTruncated )
                    {
                        yield break;
                    }
                }
            }
        }
    }

这篇关于ListView子项OwnerDraw文本的粗体部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆