Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何突出显示DrawTextEx绘制的文本后面_C#_Gdi - Fatal编程技术网

C# 如何突出显示DrawTextEx绘制的文本后面

C# 如何突出显示DrawTextEx绘制的文本后面,c#,gdi,C#,Gdi,对于自定义控件,我主要使用via(作为备用)。我正在实现增量搜索,用户输入搜索字符串并高亮显示所有匹配项。为了在文本后面绘制高亮显示,我需要计算绘制文本的每个匹配子区域的矩形 我不想在突出显示的边界处处理字体紧排,所以我不想逐段绘制文本。目的只是获取矩形坐标并绘制一个样式化的矩形 我可以用什么GDI函数来实现这一点?我已经到处搜索了。我找到的最接近的GDI函数可能是,但从文档中我并不清楚我将如何开始使用它 我知道这必须内置到Windows中;文本框在查找和突出显示任意范围的绘制文本方面没有问题。

对于自定义控件,我主要使用via(作为备用)。我正在实现增量搜索,用户输入搜索字符串并高亮显示所有匹配项。为了在文本后面绘制高亮显示,我需要计算绘制文本的每个匹配子区域的矩形

我不想在突出显示的边界处处理字体紧排,所以我不想逐段绘制文本。目的只是获取矩形坐标并绘制一个样式化的矩形

我可以用什么GDI函数来实现这一点?我已经到处搜索了。我找到的最接近的GDI函数可能是,但从文档中我并不清楚我将如何开始使用它


我知道这必须内置到Windows中;文本框在查找和突出显示任意范围的绘制文本方面没有问题。它必须在某个地方作为API公开,对吗?我非常不喜欢GDI+的文本呈现,我不想引入DirectX只是为了做这样简单的事情。

这段代码工作得很好。我还没有添加用于处理包装的功能,但它可以处理水平和垂直对齐以及带有鲜艳颜色的省略号。如果高亮部分被切断或丢失,则整个省略号将高亮显示

在Unicode这个大世界里,这个函数可能有点幼稚,所以在某个时候它可能值得研究一下。也许一个更有知识的人可以告诉我DrawText在内部使用了什么

public static class TextUtils
{
    const TextFormatFlags AlignFlags = TextFormatFlags.HorizontalCenter | TextFormatFlags.Right | TextFormatFlags.VerticalCenter | TextFormatFlags.Bottom;
    const TextFormatFlags PaddingFlags = TextFormatFlags.NoPadding | TextFormatFlags.LeftAndRightPadding;
    const TextFormatFlags EllipsisFlags = TextFormatFlags.EndEllipsis | TextFormatFlags.WordEllipsis | TextFormatFlags.PathEllipsis;
    const TextFormatFlags CalcRectFlag = (TextFormatFlags)0x400;



    public static Rectangle GetHighlightRectangle(IDeviceContext dc, string text, int highlightStart, int highlightLength, Font font, Rectangle bounds, TextFormatFlags flags = TextFormatFlags.SingleLine)
    {
        if ((flags & TextFormatFlags.SingleLine) == 0) throw new InvalidOperationException("This method only handles single line highlights. Multiline text highlights may be composed of multiple rectangles.");
        if (string.IsNullOrEmpty(text)) throw new ArgumentException("Text must not be null or empty.", "text");
        if (highlightStart < 0) throw new ArgumentOutOfRangeException("highlightStart", highlightLength, "Highlight length must be greater than or equal to zero.");
        if (highlightLength <= 0) throw new ArgumentOutOfRangeException("highlightLength", highlightLength, "Highlight length must be greater than zero.");
        if (highlightLength > text.Length - highlightStart) throw new ArgumentOutOfRangeException("highlightLength", highlightLength, "Highlight length must be less than or equal to the length of the text minus the highlight start.");



        var drawTextParams = GetTextMargins(font, flags);

        Size totalSize;

        // Determine total size and adjust for cut-off highlight.
        // We need access to the modified string in case it was trimmed.
        // Unfortunately, TextFormatFlags.ModifyString can't be used with TextRenderer.MeasureText
        // so we'll have to roll our own.
        var hdc = dc.GetHdc();
        try
        {
            var modifiedText = new StringBuilder(text);
            var rectBounds = new RECT(0, 0, bounds.Width, bounds.Height);

            var hFont = font.ToHfont();
            try
            {
                var oldFont = Gdi32.SelectObject(hdc, hFont);
                try
                {
                    User32.DrawTextEx(hdc, modifiedText, modifiedText.Length, ref rectBounds, (flags & ~AlignFlags) | CalcRectFlag | TextFormatFlags.ModifyString, ref drawTextParams);
                }
                finally
                {
                    Gdi32.SelectObject(hdc, oldFont);  
                }
            }
            finally
            {
                Gdi32.DeleteObject(hFont);
            }

            totalSize = new Size(rectBounds.Right - rectBounds.Left, rectBounds.Bottom - rectBounds.Top);

            // drawTextParams.uiLengthDrawn may be equal to text.Length even if an ellipsis was applied
            for (var i = 0; i < drawTextParams.uiLengthDrawn; i++)
                if (modifiedText[i] != text[i])
                {
                    var unchangedLength = i;
                    if (highlightStart > unchangedLength) highlightStart = unchangedLength;
                    if (highlightStart + highlightLength > unchangedLength) highlightLength = modifiedText.Length - highlightStart;
                    text = modifiedText.ToString();
                    break;
                }
        }
        finally
        {
            dc.ReleaseHdc();
        }



        // Find the end of the highlight first rather than the beginning. This avoids having to explicitly deal with kerning.
        // Kerning may move the first highlighted character closer to the previous character. As long as the two characters
        // are measured together, kerning is taken into consideration.
        // The rectangle extends to the full beginning of the highlighted text, even when kerning moves it. 
        var highlightXEnd = drawTextParams.iLeftMargin + TextRenderer.MeasureText(dc, text.Substring(0, highlightStart + highlightLength), font, bounds.Size, (flags & ~(PaddingFlags | AlignFlags | EllipsisFlags)) | TextFormatFlags.NoPadding).Width;

        // We don't care about the kerning of the first character *following* the highlight. This way the highlight is inclusive on both ends.
        // The rectangle extends to the full end of the highlighted text, regardless of how close the following character comes.
        var highlightSize = TextRenderer.MeasureText(dc, text.Substring(highlightStart, highlightLength), font, bounds.Size, (flags & ~(PaddingFlags | AlignFlags | EllipsisFlags)) | TextFormatFlags.NoPadding);

        var unalignedHighlightBounds = new Rectangle(bounds.X + highlightXEnd - highlightSize.Width, bounds.Y, highlightSize.Width, highlightSize.Height);

        return ApplyAlignment(unalignedHighlightBounds, bounds.Width - totalSize.Width, bounds.Height - totalSize.Height, flags);
    }


    // Mimic TextRenderer's margins (see WindowsGraphics.GetTextMargins)
    private static User32.DRAWTEXTPARAMS GetTextMargins(Font font, TextFormatFlags flags)
    {
        var overhangPadding = font.Height / 6f;
        const float italicPaddingFactor = 0.5f;

        switch (flags & PaddingFlags)
        {
            case TextFormatFlags.GlyphOverhangPadding:
                return new User32.DRAWTEXTPARAMS(Marshal.SizeOf(typeof(User32.DRAWTEXTPARAMS)))
                {
                    iLeftMargin = (int)Math.Ceiling(overhangPadding),
                    iRightMargin = (int)Math.Ceiling(overhangPadding * (1 + italicPaddingFactor))
                };
            case TextFormatFlags.LeftAndRightPadding:
                return new User32.DRAWTEXTPARAMS(Marshal.SizeOf(typeof(User32.DRAWTEXTPARAMS)))
                {
                    iLeftMargin = (int)Math.Ceiling(overhangPadding * 2),
                    iRightMargin = (int)Math.Ceiling(overhangPadding * (2 + italicPaddingFactor))
                };
            case TextFormatFlags.NoPadding:
                return new User32.DRAWTEXTPARAMS(Marshal.SizeOf(typeof(User32.DRAWTEXTPARAMS)))
                {
                    iLeftMargin = 0,
                    iRightMargin = 0
                };
            default:
                throw new ArgumentException("Invalid combination of padding flags.", "flags");
        }
    }


    private static Rectangle ApplyAlignment(Rectangle rect, int widthDifference, int heightDifference, TextFormatFlags flags)
    {
        switch (flags & AlignFlags)
        {
            case TextFormatFlags.Top | TextFormatFlags.Left:
                break;
            case TextFormatFlags.Top | TextFormatFlags.HorizontalCenter:
                rect.Offset(widthDifference / 2, 0);
                break;
            case TextFormatFlags.Top | TextFormatFlags.Right:
                rect.Offset(widthDifference, 0);
                break;
            case TextFormatFlags.VerticalCenter | TextFormatFlags.Left:
                rect.Offset(0, heightDifference / 2);
                break;
            case TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter:
                rect.Offset(widthDifference / 2, heightDifference / 2);
                break;
            case TextFormatFlags.VerticalCenter | TextFormatFlags.Right:
                rect.Offset(widthDifference, heightDifference / 2);
                break;
            case TextFormatFlags.Bottom | TextFormatFlags.Left:
                rect.Offset(0, heightDifference);
                break;
            case TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter:
                rect.Offset(widthDifference / 2, heightDifference);
                break;
            case TextFormatFlags.Bottom | TextFormatFlags.Right:
                rect.Offset(widthDifference, heightDifference);
                break;
            default:
                throw new ArgumentException("Invalid combination of alignment flags.", "flags");
        }

        return rect;
    }

}
公共静态类TextUtils
{
const TextFormatFlags AlignFlags=TextFormatFlags.HorizontalCenter | TextFormatFlags.Right | TextFormatFlags.VerticalCenter | TextFormatFlags.Bottom;
const TextFormatFlags PaddingFlags=TextFormatFlags.NoPadding | TextFormatFlags.LeftAndRightPadding;
const TextFormatFlags EllipsisFlags=TextFormatFlags.EndEllipsis | TextFormatFlags.WordEllipsis | TextFormatFlags.pathElipsis;
const TextFormatFlags CalcRectFlag=(TextFormatFlags)0x400;
公共静态矩形GetHighlightRectangle(IDeviceContext dc、字符串文本、int highlightStart、int highlightLength、字体、矩形边界、TextFormatFlags flags=TextFormatFlags.SingleLine)
{
if((flags&TextFormatFlags.SingleLine)==0)抛出新的InvalidOperationException(“此方法仅处理单行高亮显示。多行文本高亮显示可能由多个矩形组成”);
if(string.IsNullOrEmpty(text))抛出新的ArgumentException(“text不能为null或空。”,“text”);
如果(highlightStart<0)抛出新ArgumentOutOfRangeException(“highlightStart”,highlightLength,“highlightLength必须大于或等于零”);
如果(highlightLength text.Length-highlightStart)抛出新ArgumentOutOfRangeException(“highlightLength”,highlightLength,“高光长度必须小于或等于文本长度减去高光开始。”);
var drawTextParams=GetTextMargins(字体、标志);
大小总大小;
//确定总尺寸并调整截止高光。
//我们需要访问修改后的字符串,以防它被修剪。
//很遗憾,TextFormatFlags.ModifyString不能与TextRenderer.MeasureText一起使用
//所以我们必须自己动手。
var hdc=dc.GetHdc();
尝试
{
var modifiedText=新的StringBuilder(文本);
var rectBounds=new RECT(0,0,bounds.Width,bounds.Height);
var hFont=font.ToHfont();
尝试
{
var oldFont=Gdi32.SelectObject(hdc、hFont);
尝试
{
User32.DrawTextEx(hdc、modifiedText、modifiedText.Length、ref rectBounds、(flags&~AlignFlags)| CalcRectFlag | TextFormatFlags.ModifyString、ref drawTextParams);
}
最后
{
Gdi32.选择对象(hdc,旧字体);
}
}
最后
{
Gdi32.DeleteObject(hFont);
}
totalSize=新大小(rectBounds.Right-rectBounds.Left,rectBounds.Bottom-rectBounds.Top);
//drawTextParams.UILengthDrawed可能等于text.Length,即使应用了省略号
对于(变量i=0;iunchangedLength)highlightStart=unchangedLength;
如果(highlightStart+highlightLength>unchangedLength)highlightLength=modifiedText.Length-highlightStart;
text=modifiedText.ToString();
打破
}
}
最后
{
dc.ReleaseHdc();
}
//首先找到突出显示的结尾,而不是开始。这避免了显式处理紧排。
//紧排可能会将第一个高亮显示的字符移近前一个字符。只要这两个字符
//在一起测量时,会考虑字距调整。
//矩形延伸到高亮显示文本的完整开头,即使在紧排移动它时也是如此。
var highlightXEnd=drawTextParams.iLeftMargin+TextRenderer.MeasureText(dc,text.Substring(0,highlightStart+highlightLength),字体,边界.Size,(flags和~(PaddingFlags | AlignFlags | EllipsisFlags))| TextFormatFlags.NoPadding).Width;
//我们不关心突出部分后面的第一个字符*的字距。这样突出部分的两端都包含在内。
//无论以下字符的距离有多近,矩形都会延伸到高亮显示文本的最末端。
var highlightSize=TextRenderer.MeasureText(dc,text.Substring(highlightStart,highlightLength),字体,bounds.Size,(flags&~(PaddingFlags | AlignFlags |省略号