【发布时间】:2020-11-17 04:12:28
【问题描述】:
我的问题
我正在尝试创建一个TreeView,它将与搜索词匹配的节点文本部分加粗。我的代码是从this question 采用的。无论是否使用ImageList,我都有相同/相似的问题,但我将在此处发布未使用的版本。当我渲染我的文本时,我会得到这样的东西,其中文本的最后一部分被截断,但 仅 用于某些节点。即 版本 看起来不错,但其余的文本从边界截断的数量不同。
我认为我的TextFormatFlags 标志会影响这一点,但如果我在文本的测量/渲染期间没有传递这些标志,节点会在左侧切断。
如果我绘制粗体文本,还会引入 垂直 间距问题。你可以看到,当我使用 Plan 作为搜索词时,它比文本的其余部分高一点。
如果我选择一个节点,您会看到我再次遇到水平间距问题。
我的问题
- 不加粗体的节点渲染的水平尺寸问题
- 用粗体部分渲染的节点的垂直尺寸问题
- 使用粗体 和 活动的节点呈现的水平尺寸问题。
更新代码
感谢@jimi,我能够巩固一些东西。我在他回答时接近了,但以下是我根据他的建议所做的更改。我确实做了一些与他不同的事情。
-
当
( formClosing || e.Bounds.X == -1 )为真时,我会立即退出tree_DrawNode,以避免出现一些图形故障。你可以在下面看到我的评论。 -
我喜欢
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;
}
}
【问题讨论】:
-
e.Node.Bounds不能用作粗体项目的高亮矩形,因为e.Node不知道它使用的是不同的字体。您必须使用TextRendered.MeasureText调整该矩形的大小 -
现在正在处理这个问题,但在渲染文本之前我不知道大小。但如果我 FillRectangle 那么它在文本之上。我唯一的选择是只循环一次测量,然后绘制背景,然后再次循环并测量/绘制文本吗?
-
先测量,然后绘图——就这么简单。我无法从您的 for-each 循环中得出正面或反面。您正在绘制一个节点,为什么会出现循环?
-
我在节点文本中循环“匹配”,所以我可以用粗体和其他部分常规绘制匹配。因此,如果您搜索“N”,PlanInfo 将看起来像 PlanInfo。
-
哦,原来是
RenderNodeCharacter。在这种情况下,您最好编写一个小字母字典,其中包含粗体和简单的预渲染尺寸。由于TextFormatFlags.Top,垂直对齐已关闭。您可能应该改用VerticalCenter(我猜)。
标签: c# .net winforms graphics treeview