我创建了一个装饰器,将其应用于文本框,这是为了我们定制的拼写检查。 每当出现未知单词时,装饰器都会使用 Drawingcontext.drawline 添加下划线。这是代码:
_highlighterPen = new Pen(new SolidColorBrush(Colors.Red), 2)
{
EndLineCap = PenLineCap.Square,
StartLineCap = PenLineCap.Square
};
private void HighlightWord(DrawingContext drawingContext, int characterIndex, int wordLength)
{
if (characterIndex + wordLength > _textbox.Text.Length)
return;
var start = _textbox.GetRectFromCharacterIndex(characterIndex);
var end = _textbox.GetRectFromCharacterIndex(characterIndex + (wordLength - 1), true);
if (start.Y < 0 || start.BottomRight.Y > _textbox.ActualHeight)
return;
var wrapCorrectedStartRect = start.BottomRight.Y != end.BottomRight.Y ? new Rect(2, start.Y, start.Width, start.Height) : start;
var highlightArea = Rect.Union(wrapCorrectedStartRect, end);
drawingContext.DrawLine(_highlighterPen, highlightArea.BottomLeft, highlightArea.BottomRight);
if (start.BottomRight.Y != end.BottomRight.Y)
HighlightWrappedWord(drawingContext, characterIndex, end, wordLength);
}
我希望实现的是线条的鼠标悬停效果(不影响其他线条),而不必使视觉效果无效并再次重新绘制所有内容。请问有人可以帮忙吗?
您可以覆盖装饰器上的
UIElemen.OnMouseEnter
和 UIElement.OnMouseLeave
。
因为您无法修改旧的
DrawingContext
,所以您必须使 Adorner
无效才能将新内容添加到 DrawingContex
。
因为您不想使装饰器无效,所以您必须在专用的
DrawingContext
上绘制才能渲染效果。
您可以通过添加子效果
DrawingContext
来创建新的 Visual
。例如, DrawingVisual
将显式创建一个新的 DataContext
,而添加控件将隐式创建 DrawingContext
(控件将创建它)。
要实现此目的,您必须将您的
Adorner
变成 Visual
主机。然后,您可以通过向 Visual
添加/删除 Adorner
对象来添加/删除效果。这可以是任何 Visual
例如Button
或其他显式 DrawingContext
控制绘图,如 DrawingVisual
。
如果添加控件,则必须执行布局过程(至少是排列过程 - 在非常复杂的场景中,您可能还需要执行测量过程)。 如果您添加绘图 (
DrawingVisual
),渲染引擎将拾取新的视觉子项 (DrawingContext
),并在添加到视觉树后自动渲染它。
以下示例展示了如何启用
Visual
托管以及如何在鼠标悬停在 Button
上时渲染绘图(矩形)和 Adorner
:
// The 'Adorner' that also acts as a Visual host
private class TextBoxAdorner : Adorner
{
private readonly VisualCollection visualChildren;
private readonly Button mouseOverButton;
public TextBoxAdorner(UIElement adornedElement) : base(adornedElement)
{
this.visualChildren = new VisualCollection(this);
this.IsHitTestVisible = true;
// The control we want to show on mouse over
this.mouseOverButton = new Button()
{
Content = "MouseOverButton",
Background = Brushes.Red
};
// Add interaction behavior to the button
this.mouseOverButton.Click += OnButtonClicked;
}
// Mandatory override
protected override int VisualChildrenCount
=> this.visualChildren.Count;
// Mandatory override
protected override Visual GetVisualChild(int index)
=> this.visualChildren[index];
// Show mouse hover effects
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
var drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create the drawing
var rect = new Rect(new Size(200, 200));
drawingContext.DrawRectangle(Brushes.Blue, null, rect);
drawingContext.Close();
// Show the drawing by adding it as a DrawingVisual.
// This will not trigger OnRender!
// (It will trigger OnRender on the visual child).
this.visualChildren.Add(drawingVisual);
// Add a control
this.visualChildren.Add(this.mouseOverButton);
// Show it by triggering an arrange layout pass
// This will not trigger OnRender!
// (It will trigger OnRender on the visual child).
InvalidateArrange();
}
// Hide mouse hover effects
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
// Clear the mouse over effects
this.visualChildren.Clear();
}
// Arrange the controls that were added to the adorner's visual tree
protected override Size ArrangeOverride(Size finalSize)
{
// Arrange the mouse over controls
this.mouseOverButton?.Measure(finalSize);
var renderPosition = new Rect(this.mouseOverButton.DesiredSize);
this.mouseOverButton?.Arrange(renderPositionn);
return base.ArrangeOverride(finalSize);
}
// Render the Adorner itself
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
// TODO::Perform your normal rendering here
HighlightWord(drawingContext, 1, 1);
}
}