在wpf richtextbox中设置文本范围的工具提示

时间:2014-12-25 17:57:04

标签: c# richtextbox wpf-4.0

我正在尝试在richtextbox上的任意文本范围上设置工具提示。这可能吗?如果是这样,我怎么做,例如通过参数"来自"和"到" as(int)索引。

由于

1 个答案:

答案 0 :(得分:0)

您可以使用以下内容作为起点:

  1. 添加对System.Windows.Interactivity

  2. 的引用
  3. 将以下类添加到项目中:

    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();
            }
        }
    }
    
  4. 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>
    

    或者,您可以将TextRangeToolTipCollection绑定到Ranges属性,如下所示:

    <RichTextBox Document="{Binding Document}">
        <i:Interaction.Behaviors>
            <myapp:ToolTipBehavior Ranges="{Binding RangeToolTips}" />
        </i:Interaction.Behaviors>
    </RichTextBox>
    
  5. 获得正确的位置有点棘手,因为WPF会计算符号,而不是字符。您可以扩展TextRangeToolTip类,使其具有TextPointerTextRange类型的属性,并使用您的FlowDocument实例构建它。