none
WPF:如何在RichTextBox中选中一段文本后显示工具条并根据与鼠标的距离实现淡入淡出效果? RRS feed

  • 问题

  • 各位大神:

    在WPF项目中要实现一个类似WPS Word中选中文本后弹出mini工具条的功能。在RichTextBox选中文本后可以用Popup实现弹出,但有如下2点不知如何实现:

    1. 工具条显示位置要在选中文本块的上方
    2. 工具条要根据与鼠标的距离渐显、渐隐,比如鼠标与工具条在一定的距离内:逐渐远离工具条后,工具条渐隐,超过这个距离后不再显示;鼠标在这个距离内逐渐靠近工具条后,工具条渐显

    如下图所示:

    2019年6月20日 2:38

答案

  • 各位大神:

    在WPF项目中要实现一个类似WPS Word中选中文本后弹出mini工具条的功能。在RichTextBox选中文本后可以用Popup实现弹出,但有如下2点不知如何实现:

    1. 工具条显示位置要在选中文本块的上方
    2. 工具条要根据与鼠标的距离渐显、渐隐,比如鼠标与工具条在一定的距离内:逐渐远离工具条后,工具条渐隐,超过这个距离后不再显示;鼠标在这个距离内逐渐靠近工具条后,工具条渐显

    如下图所示:

    Hi  银光海阁,

    跟你的描述,你可以参考下面的建议
      
    >工具条显示位置要在选中文本块的上方

    可以在RichTextBox中使用MouseLeftButtonUp事件,当有文本选中时(rtb.Selection.Start != rtb.Selection.End),获取鼠标的此刻的positionA,然后用Canvas中设置位置的方法,设置此时PopUp的位置。

    >工具条要根据与鼠标的距离渐显、渐隐,比如鼠标与工具条在一定的距离内:逐渐远离工具条后,工具条渐隐,超过这个距离后不再显示;鼠标在这个距离内逐渐靠近工具条后,工具条渐显

    在窗体中使用MouseMove事件和Animation来实现,当PopUp的IsOpen="True"的时候实时获取鼠标的positionB。根据positionB和positionA之间的距离设置PopUp的Opacity的值,当Opacity值等于0的时候,将PopUp的IsOpen设置为"False".


    Best regards

    Yong Lu

     

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年6月20日 9:53
    版主
  • 我自己写了小demo,没用popup,用adorner弄

    中间黑色的部分代表真正的工具栏。

    简单写的,给你个思路。

    具体细节还需要调整。

    和附加属性或者依赖属性的组合式使用,或者用popup作为弹窗,这样有更好的针对性的内容

    xaml代码

    <Window x:Class="ADO_TOOL.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ADO_TOOL"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
    
        <Grid x:Name="adohost">
            <RichTextBox LostKeyboardFocus="RTB_LostKeyboardFocus"  VerticalAlignment="Center"  x:Name="RTB"  PreviewMouseLeftButtonUp="RichTextBox_PreviewMouseLeftButtonUp"  >
                <FlowDocument  >
                    <Paragraph >
                        <Run    Text="测试显示tool"/>
                    </Paragraph>
                </FlowDocument>
            </RichTextBox>
        </Grid>
    </Window>

    adorner代码类

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    namespace ADO_TOOL
    {
        public class ado_Gird : Adorner
        {
    
            private VisualCollection collection;
    
            protected override int VisualChildrenCount => collection.Count;
    
    
            protected override Visual GetVisualChild(int index) => collection[index];
    
    
            protected override Size MeasureOverride(Size constraint) => base.MeasureOverride(constraint);
    
            protected override Size ArrangeOverride(Size finalSize)
            {
    
                _gird.Arrange(new Rect(finalSize));
    
                var f = host as FrameworkElement;
    
                _gird.Margin = new Thickness(left, 0-top-_gird.Height,0,0);
               
               return base.ArrangeOverride(finalSize);
    
            }
            private  Grid _gird;
    
            private UIElement host;
    
            private double left;
            private double top;
    
            public   ado_Gird(UIElement adornedElement,double left,double top) :this(adornedElement)
            {
                this.left= left;
                this.top = top;
            }
    
            //h1是外部grid-内部grid的平均高度
            //h2是外部grid-内部grid的平均宽度
            readonly int h1 = 20,h2=24;
            public ado_Gird(UIElement adornedElement) : base(adornedElement)
            {
                collection = new VisualCollection(this);
                host = adornedElement;
                this._gird = new Grid();
    
                _gird.Height = 50;
    
                _gird.Width = 128;
    
                _gird.HorizontalAlignment = HorizontalAlignment.Left;
    
                _gird.Background = new SolidColorBrush(Colors.Red);
    
                Grid g2 = new Grid();
    
                g2.Height = 10;
    
                g2.Width = 80;
    
                g2.Background = new SolidColorBrush(Colors.Black);
    
                _gird.Children.Add(g2);
    
                _gird.MouseMove += _gird_MouseMove;
    
                _gird.MouseLeave += _gird_MouseLeave;
    
                _gird.Opacity = 0.5;
         
                collection.Add(_gird);
            }
    
            private void _gird_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
            {
                _gird.Opacity = 0.1;
            }
    
            private void _gird_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
            {
                var point = e.GetPosition(_gird);
                var f = host as FrameworkElement;
                if (_gird.Width - point.X <= h2)
                {
                      _gird.Opacity = 1-((h2 -( _gird.Width - point.X)) / h2);
                }
                if(_gird.Height - point.Y <= h1)
                {
                    _gird.Opacity = 1 - ((h1 - (_gird.Height - point.Y)) / h1);
                }
                if (point.X>0&&point.X<=h2)
                {
                    _gird.Opacity = 1-((h2 - point.X) / h2);
                }
                if (point.Y > 0 && point.Y <= h1)
                {
                   _gird.Opacity = 1 - (h1 - point.Y) / h1;
                }
                
            }
        }
    }
    

    mainwindow.xaml.cs

     AdornerLayer layer;
            private void RichTextBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                var point = e.GetPosition(RTB);
                if (layer != null)
                {
                    var b = layer.GetAdorners(adohost);
                    if(b!=null)
                    if(b.Count()>0)
                       layer.Remove(b[0]);
                }
                layer = AdornerLayer.GetAdornerLayer(adohost);
                var ado = new ado_Gird(adohost, point.X,RTB.ActualHeight);
                layer.Add(ado);
            }     
    
            private void RTB_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
               
                if (layer != null)
                {
                    var b = layer.GetAdorners(adohost);
                    if (b != null)
                        if (b.Count() > 0)
                        layer.Remove(b[0]); ;
                }
            }

    2019年6月20日 16:43

全部回复

  • 各位大神:

    在WPF项目中要实现一个类似WPS Word中选中文本后弹出mini工具条的功能。在RichTextBox选中文本后可以用Popup实现弹出,但有如下2点不知如何实现:

    1. 工具条显示位置要在选中文本块的上方
    2. 工具条要根据与鼠标的距离渐显、渐隐,比如鼠标与工具条在一定的距离内:逐渐远离工具条后,工具条渐隐,超过这个距离后不再显示;鼠标在这个距离内逐渐靠近工具条后,工具条渐显

    如下图所示:

    Hi  银光海阁,

    跟你的描述,你可以参考下面的建议
      
    >工具条显示位置要在选中文本块的上方

    可以在RichTextBox中使用MouseLeftButtonUp事件,当有文本选中时(rtb.Selection.Start != rtb.Selection.End),获取鼠标的此刻的positionA,然后用Canvas中设置位置的方法,设置此时PopUp的位置。

    >工具条要根据与鼠标的距离渐显、渐隐,比如鼠标与工具条在一定的距离内:逐渐远离工具条后,工具条渐隐,超过这个距离后不再显示;鼠标在这个距离内逐渐靠近工具条后,工具条渐显

    在窗体中使用MouseMove事件和Animation来实现,当PopUp的IsOpen="True"的时候实时获取鼠标的positionB。根据positionB和positionA之间的距离设置PopUp的Opacity的值,当Opacity值等于0的时候,将PopUp的IsOpen设置为"False".


    Best regards

    Yong Lu

     

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年6月20日 9:53
    版主
  • 我自己写了小demo,没用popup,用adorner弄

    中间黑色的部分代表真正的工具栏。

    简单写的,给你个思路。

    具体细节还需要调整。

    和附加属性或者依赖属性的组合式使用,或者用popup作为弹窗,这样有更好的针对性的内容

    xaml代码

    <Window x:Class="ADO_TOOL.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ADO_TOOL"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
    
        <Grid x:Name="adohost">
            <RichTextBox LostKeyboardFocus="RTB_LostKeyboardFocus"  VerticalAlignment="Center"  x:Name="RTB"  PreviewMouseLeftButtonUp="RichTextBox_PreviewMouseLeftButtonUp"  >
                <FlowDocument  >
                    <Paragraph >
                        <Run    Text="测试显示tool"/>
                    </Paragraph>
                </FlowDocument>
            </RichTextBox>
        </Grid>
    </Window>

    adorner代码类

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    namespace ADO_TOOL
    {
        public class ado_Gird : Adorner
        {
    
            private VisualCollection collection;
    
            protected override int VisualChildrenCount => collection.Count;
    
    
            protected override Visual GetVisualChild(int index) => collection[index];
    
    
            protected override Size MeasureOverride(Size constraint) => base.MeasureOverride(constraint);
    
            protected override Size ArrangeOverride(Size finalSize)
            {
    
                _gird.Arrange(new Rect(finalSize));
    
                var f = host as FrameworkElement;
    
                _gird.Margin = new Thickness(left, 0-top-_gird.Height,0,0);
               
               return base.ArrangeOverride(finalSize);
    
            }
            private  Grid _gird;
    
            private UIElement host;
    
            private double left;
            private double top;
    
            public   ado_Gird(UIElement adornedElement,double left,double top) :this(adornedElement)
            {
                this.left= left;
                this.top = top;
            }
    
            //h1是外部grid-内部grid的平均高度
            //h2是外部grid-内部grid的平均宽度
            readonly int h1 = 20,h2=24;
            public ado_Gird(UIElement adornedElement) : base(adornedElement)
            {
                collection = new VisualCollection(this);
                host = adornedElement;
                this._gird = new Grid();
    
                _gird.Height = 50;
    
                _gird.Width = 128;
    
                _gird.HorizontalAlignment = HorizontalAlignment.Left;
    
                _gird.Background = new SolidColorBrush(Colors.Red);
    
                Grid g2 = new Grid();
    
                g2.Height = 10;
    
                g2.Width = 80;
    
                g2.Background = new SolidColorBrush(Colors.Black);
    
                _gird.Children.Add(g2);
    
                _gird.MouseMove += _gird_MouseMove;
    
                _gird.MouseLeave += _gird_MouseLeave;
    
                _gird.Opacity = 0.5;
         
                collection.Add(_gird);
            }
    
            private void _gird_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
            {
                _gird.Opacity = 0.1;
            }
    
            private void _gird_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
            {
                var point = e.GetPosition(_gird);
                var f = host as FrameworkElement;
                if (_gird.Width - point.X <= h2)
                {
                      _gird.Opacity = 1-((h2 -( _gird.Width - point.X)) / h2);
                }
                if(_gird.Height - point.Y <= h1)
                {
                    _gird.Opacity = 1 - ((h1 - (_gird.Height - point.Y)) / h1);
                }
                if (point.X>0&&point.X<=h2)
                {
                    _gird.Opacity = 1-((h2 - point.X) / h2);
                }
                if (point.Y > 0 && point.Y <= h1)
                {
                   _gird.Opacity = 1 - (h1 - point.Y) / h1;
                }
                
            }
        }
    }
    

    mainwindow.xaml.cs

     AdornerLayer layer;
            private void RichTextBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                var point = e.GetPosition(RTB);
                if (layer != null)
                {
                    var b = layer.GetAdorners(adohost);
                    if(b!=null)
                    if(b.Count()>0)
                       layer.Remove(b[0]);
                }
                layer = AdornerLayer.GetAdornerLayer(adohost);
                var ado = new ado_Gird(adohost, point.X,RTB.ActualHeight);
                layer.Add(ado);
            }     
    
            private void RTB_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
               
                if (layer != null)
                {
                    var b = layer.GetAdorners(adohost);
                    if (b != null)
                        if (b.Count() > 0)
                        layer.Remove(b[0]); ;
                }
            }

    2019年6月20日 16:43