WPF 装饰器鼠标悬停

问题描述 投票:0回答:1

我创建了一个装饰器,将其应用于文本框,这是为了我们定制的拼写检查。 每当出现未知单词时,装饰器都会使用 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);
    }

我希望实现的是线条的鼠标悬停效果(不影响其他线条),而不必使视觉效果无效并再次重新绘制所有内容。请问有人可以帮忙吗?

wpf mouseover adorner
1个回答
0
投票

您可以覆盖装饰器上的

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);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.