[英]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: 您可以使用以下内容作为起点:
Add a reference to System.Windows.Interactivity
. 添加对System.Windows.Interactivity
的引用。
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(); } } }
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
类,使其具有TextPointer
或TextRange
类型的属性,并使用FlowDocument
实例构造它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.