【问题标题】:ListView SubItem OwnerDraw Bold Part of the TextListView SubItem OwnerDraw 粗体部分文本
【发布时间】:2020-11-18 14:21:44
【问题描述】:

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

到目前为止,我的 TreeView question 和我的 ComboBox question 关于所有者绘图帮助塑造了代码。我不需要任何背景或边框图,因此它比我看到人们提出的其他一些问题更简单,但我有一些问题,我目前注意到了。

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

在下面的系列中,Address 1 是一个完整的字符串。其他两项的字符串为 110 MapleAve​​nue

关于...,有没有办法让整个字符串作为一个单独的实体?在我的简短测试中,我还没有发现任何其他问题,但如果需要,我很乐意接受一些建议。

调整大小之前

第一次调整大小 - 只有地址 1 显示 ...

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

第三次调整大小 - 1.1 亿个节目 ...

最终调整大小 - 每个“字符串”显示 ...

代码

    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;
                }
            }
        }
    }

【问题讨论】:

    标签: c# .net winforms listview graphics


    【解决方案1】:

    所以在考虑了一下之后,我意识到每个文本部分(粗体和非粗体)都是根据“整个子项”Bounds.Size 来衡量的。我不得不不断减小TextRenderer.MeasureText 的允许大小,以便它知道何时放入...,然后在使用... 呈现任何项目后立即停止处理其他字符串。所以我的BuildDrawingsString 必须考虑到这一点,并且它还必须为每个部分返回一个AllowedWidth,以便对TextRenderer.DrawText 的实际调用每次也将使用正确的Size

        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;
                        }
                    }
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      相关资源
      最近更新 更多