简体   繁体   English

在WPF RichTextBox中的文本范围上设置工具提示

[英]Set tooltip on range of text in wpf richtextbox

I am trying to set a tooltip on an arbitrary range of text on a richtextbox. 我试图在Richtextbox上的任意范围的文本上设置工具提示。 Is this possible? 这可能吗? If so how would I do it eg by passing in parameters "from" and "to" as (int) indexes. 如果是这样,我将如何做,例如通过将参数“ from”和“ to”作为(int)索引传入。

Thanks 谢谢

You could use the following as a starting point: 您可以使用以下内容作为起点:

  1. Add a reference to System.Windows.Interactivity . 添加对System.Windows.Interactivity的引用。

  2. Add the following classes to your project: 将以下类添加到您的项目中:

     public class TextRangeToolTip { public int StartPosition { get; set; } public int Length { get; set; } public object ToolTip { get; set; } internal bool IsInRange(int position) { return this.StartPosition <= position && position < this.StartPosition + this.Length; } } public class TextRangeToolTipCollection : ObservableCollection<TextRangeToolTip> {} [ContentProperty("Ranges")] public class ToolTipBehavior : Behavior<RichTextBox> { private const int ToolTipHideMouseDelta = 9; public static readonly DependencyProperty RangesProperty = DependencyProperty.Register("Ranges", typeof(TextRangeToolTipCollection), typeof (ToolTipBehavior), new PropertyMetadata(OnRangesChanged)); private readonly DispatcherTimer timer; private readonly ToolTip toolTip; private Point lastMousePosition; public TextRangeToolTipCollection Ranges { get { return (TextRangeToolTipCollection)this.GetValue(RangesProperty) ?? (this.Ranges = new TextRangeToolTipCollection()); } set { this.SetValue(RangesProperty, value); } } public ToolTipBehavior() { this.Ranges = new TextRangeToolTipCollection(); this.timer = new DispatcherTimer(); this.timer.Tick += this.TimerOnTick; this.timer.Interval = TimeSpan.FromSeconds(1); this.toolTip = new ToolTip {Placement = PlacementMode.Relative}; } protected override void OnAttached() { this.AssociatedObject.ToolTip = this.toolTip; this.toolTip.PlacementTarget = this.AssociatedObject; ToolTipService.SetIsEnabled(this.AssociatedObject, false); this.AssociatedObject.MouseMove += this.AssociatedObjectOnMouseMove; } protected override void OnDetaching() { this.timer.Stop(); this.toolTip.PlacementTarget = null; this.AssociatedObject.ToolTip = null; this.AssociatedObject.ClearValue(ToolTipService.IsEnabledProperty); this.AssociatedObject.MouseMove -= this.AssociatedObjectOnMouseMove; } private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs mouseEventArgs) { Point currentMousePosition = mouseEventArgs.GetPosition(this.AssociatedObject); if (this.AssociatedObject.IsMouseCaptured) { Vector delta = currentMousePosition - this.lastMousePosition; if (delta.X*delta.X + delta.Y*delta.Y <= ToolTipHideMouseDelta) { this.toolTip.HorizontalOffset = currentMousePosition.X + 10; this.toolTip.VerticalOffset = currentMousePosition.Y + 10; return; } this.AssociatedObject.ReleaseMouseCapture(); this.toolTip.IsOpen = false; } if (this.AssociatedObject.IsMouseOver) { this.lastMousePosition = currentMousePosition; this.timer.Stop(); this.timer.Start(); } } private static void OnRangesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ToolTipBehavior) d).OnRangesChanged((IEnumerable<TextRangeToolTip>) e.OldValue, (IEnumerable<TextRangeToolTip>) e.NewValue); } private void OnRangesChanged(IEnumerable<TextRangeToolTip> oldRanges, IEnumerable<TextRangeToolTip> newRanges) { var oldObservable = oldRanges as INotifyCollectionChanged; if (oldObservable != null) { CollectionChangedEventManager.RemoveHandler(oldObservable, this.OnRangesCollectionChanged); } var newObservable = newRanges as INotifyCollectionChanged; if (newObservable != null) { CollectionChangedEventManager.AddHandler(newObservable, this.OnRangesCollectionChanged); } this.UpdateToolTip(); } private void OnRangesCollectionChanged( object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) { this.UpdateToolTip(); } private bool SetToolTipData() { if (this.Ranges == null) { return false; } TextPointer pointer = this.AssociatedObject.GetPositionFromPoint(this.lastMousePosition, false); if (pointer == null) { return false; } int position = this.AssociatedObject.Document.ContentStart.GetOffsetToPosition(pointer); TextRangeToolTip matchingRange = this.Ranges.FirstOrDefault(r => r.IsInRange(position)); if (matchingRange == null) { return false; } this.toolTip.Content = matchingRange.ToolTip; return true; } private void TimerOnTick(object sender, EventArgs eventArgs) { this.timer.Stop(); if (this.AssociatedObject.IsMouseOver && this.SetToolTipData()) { this.toolTip.IsOpen = true; this.AssociatedObject.CaptureMouse(); } } private void UpdateToolTip() { if (this.AssociatedObject != null && this.AssociatedObject.IsMouseCaptured && !this.SetToolTipData()) { this.toolTip.IsOpen = false; this.AssociatedObject.ReleaseMouseCapture(); } } } 
  3. Use it on your RichTextBox like this: 像这样在您的RichTextBox上使用它:

     <RichTextBox> <i:Interaction.Behaviors> <myapp:ToolTipBehavior> <myapp:TextRangeToolTip StartPosition="10" Length="4" ToolTip="some" /> <myapp:TextRangeToolTip StartPosition="15" Length="4" ToolTip="text" /> </myapp:ToolTipBehavior> </i:Interaction.Behaviors> <FlowDocument> <Paragraph>This is some text. This is some other text.</Paragraph> </FlowDocument> </RichTextBox> 

    Alternatively, you can bind a TextRangeToolTipCollection to the Ranges property like this: 或者,您可以将TextRangeToolTipCollection绑定到Ranges属性,如下所示:

     <RichTextBox Document="{Binding Document}"> <i:Interaction.Behaviors> <myapp:ToolTipBehavior Ranges="{Binding RangeToolTips}" /> </i:Interaction.Behaviors> </RichTextBox> 

Getting the positions right is a bit tricky, because WPF counts symbols, not characters. 正确定位位置有些棘手,因为WPF计数符号而不是字符。 You could extend the TextRangeToolTip class to have properties of type TextPointer or TextRange and construct it using your FlowDocument instance. 您可以扩展TextRangeToolTip类,使其具有TextPointerTextRange类型的属性,并使用FlowDocument实例构造它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM