我的问题
我正在尝试创建一个TreeView
这会将与搜索词匹配的节点文本部分加粗。我的代码被采用于这个问题。我有相同/类似的问题,有或没有ImageList
使用过,但我会在这里发布未使用的版本。当我渲染文本时,我得到类似这样的结果,其中文本的最后一部分被切断,但是only对于某些节点。 IE。Version看起来不错,但其余的文本从边界上被截断的数量不同。
我认为我的TextFormatFlags
标志正在影响这一点,但如果我在文本的测量/渲染期间不传递这些标志,则节点会在左侧切断。
如果我绘制粗体文本,我还会有vertical引入了间距问题。当我使用时你可以看到Plan作为搜索词,它比文本的其余部分要高一些。
如果我选择一个节点,您可以看到我再次遇到水平间距问题。
我的问题
- 没有粗体渲染的节点水平尺寸问题
- 使用粗体部分渲染的节点的垂直尺寸问题
- 粗体渲染节点的水平尺寸问题and active.
更新代码
感谢@jimi,我能够确定一些东西。当他回答时我很接近,但以下是我根据他的建议所做的更改。我确实做了一些与他不同的事情。
-
我立即退出tree_DrawNode
when ( formClosing || e.Bounds.X == -1 )
是真的,以避免一些图形故障。你可以在下面看到我的评论。
-
我喜欢如何BuildDrawingString
清理了代码。我添加了一个可计算的 Width 属性,此外还修复了有关返回匹配文本的错误。
-
当节点具有焦点时,我绘制了突出显示背景;当节点没有焦点时,我绘制了窗口背景,以更好地模拟默认值TreeView
行为。特别是当鼠标在一个节点上向下移动并在其他地方向上移动时。
-
而不是仅仅使用e.Bounds
为了绘制背景矩形,我根据 e.Node.Bounds x/y、渲染文本所需的宽度和一点填充创建了自己的矩形。
private void tree_DrawNode( object sender, DrawTreeNodeEventArgs e )
{
var textPadding = 2;
// formClosing - don't need to redraw when shutting down, avoids seeing a little glitch with text offset
// e.Bounds.X == -1 - when form loads, all *non-top level* nodes seem to draw on top of each other on first line
// causing a big 'black blur' to happen when form loads b/c text is mashed together
if ( formClosing || e.Bounds.X == -1 )
{
return;
}
using ( var boldFont = new Font( tree.Font, FontStyle.Bold ) )
{
var stringParts = BuildDrawingString( e, fieldSearch.Text, boldFont ).ToArray();
// To better emulate default behavior, draw the 'selected' look only when focused, so if
// you click down on item, originally selected item draws 'normal' and item clicking on is 'selected'
// and if you let up on mouse outside of node, it reverts back to how it was.
var isSelected = e.State.HasFlag( TreeNodeStates.Focused );
var color = isSelected ? Color.White : tree.ForeColor;
// Use e.NodeBounds X,Y and width of measured text with just a little bit of
// padding on left and right, e.Bounds was too wide.
var nodeRectangle = new Rectangle(
e.Node.Bounds.X,
e.Node.Bounds.Y,
stringParts.Sum( p => p.Width ) + textPadding * 2,
e.Node.Bounds.Height
);
e.Graphics.FillRectangle( isSelected ? SystemBrushes.Highlight : SystemBrushes.Window, nodeRectangle );
if ( isSelected )
{
using ( var focusPen = new Pen( Color.Black ) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot } )
{
nodeRectangle.Size = new Size( nodeRectangle.Width - 1, nodeRectangle.Height - 1 );
e.Graphics.DrawRectangle( focusPen, nodeRectangle );
}
}
var point = new Point( e.Node.Bounds.X + textPadding, e.Node.Bounds.Y );
foreach ( var part in stringParts )
{
var font = part.Selected ? boldFont : tree.Font;
RenderNodeText( part.Text, e, font, point, color );
point.Offset( part.Width, 0 );
}
}
}
private void RenderNodeText( string text, DrawTreeNodeEventArgs e, Font font, Point offset, Color color )
{
var size = e.Node.Bounds.Size;
var rect = new Rectangle( offset, size );
TextRenderer.DrawText( e.Graphics, text, font, rect, color, e.Node.BackColor, treeFlags );
}
private IEnumerable<(string Text, bool Selected, int Width)> BuildDrawingString( DrawTreeNodeEventArgs e, string pattern, Font boldFont )
{
var itemContent = e.Node.Text;
int measureText( string t, bool s ) => TextRenderer.MeasureText( e.Graphics, t, s ? boldFont : tree.Font, e.Bounds.Size, treeFlags ).Width;
if ( pattern.Length == 0 )
{
yield return (itemContent, false, measureText( itemContent, false ));
}
else
{
var matches = Regex.Split( itemContent, $"(?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 = itemContent.Substring( currentCharacter, patternLength );
yield return (
matchText,
true,
measureText( matchText, true )
);
currentCharacter += patternLength;
}
}
}
}
新花样
我将此处创建的所有最终代码从 WinForms 应用程序移至 VSTO Word AddIn 项目/表单内的表单,并且由于某种原因字体渲染有所不同。
- 一般的字体(普通字体)看起来更细更小。
- 粗体字体看起来比普通字体偏移得更高一些。
在下图中,最上面的表单是来自 Word 的表单,第二个表单(Form1 的标题)是我的 WinForms 应用程序。作为 VSTO 加载项运行时是否存在兼容性问题或其他问题?
原始代码
private void Form1_Load( object sender, EventArgs e )
{
tree.DrawMode = TreeViewDrawMode.OwnerDrawText;
tree.DrawNode += tree_DrawNode;
tree.Font = new Font( "Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 0 );
// tree.ImageList = imageList;
tree.Nodes.Add( "PlanInfo" );
tree.Nodes[ 0 ].Nodes.Add( "Version" );
tree.Nodes[ 0 ].Nodes.Add( "Plan Name" );
tree.Nodes[ 0 ].Nodes.Add( "Plan Sponsor" );
}
TextFormatFlags treeFlags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.NoPadding;
private void tree_DrawNode( object sender, DrawTreeNodeEventArgs e )
{
var currentX = 0;
var searchText = e.Node.Text;
var searchTerm = fieldSearch.Text;
var matches = Regex.Split( searchText, "(?i)" + searchTerm );
var point = new Point( e.Node.Bounds.X + currentX, e.Node.Bounds.Y );
var isSelected = ( e.State & TreeNodeStates.Selected ) != 0;
var color = isSelected ? Color.White : tree.ForeColor;
if ( isSelected )
{
e.Graphics.FillRectangle( SystemBrushes.Highlight, e.Node.Bounds );
}
if ( !string.IsNullOrEmpty( searchTerm ) && matches != null )
{
var currentCharacter = 0;
var currentMatch = 0;
var keyLength = searchTerm.Length;
foreach ( var m in matches )
{
if ( !string.IsNullOrEmpty( m ) )
{
point.Offset(
RenderNodeText( m, e, FontStyle.Regular, point, color ).Width,
0
);
currentCharacter += m.Length;
}
currentMatch++;
if ( currentMatch < matches.Length || ( string.IsNullOrEmpty( m ) && currentMatch == 1 ) )
{
var boldText = searchText.Substring( currentCharacter, keyLength );
point.Offset(
RenderNodeText( boldText, e, FontStyle.Bold, point, color ).Width,
0
);
currentCharacter += keyLength;
}
}
}
else
{
RenderNodeText( e.Node.Text, e, FontStyle.Regular, point, color );
}
}
private Size RenderNodeText( string text, DrawTreeNodeEventArgs e, FontStyle altStyle, Point offset, Color color )
{
using ( var font = new Font( tree.Font, altStyle ) )
{
var size = e.Node.Bounds.Size;
var textWidth = TextRenderer.MeasureText( e.Graphics, text, font, size, treeFlags );
var rect = new Rectangle( offset, size );
TextRenderer.DrawText( e.Graphics, text, font, rect, color, Color.Transparent, treeFlags );
return textWidth;
}
}