仿Word的支持横轴竖轴的WPF 标尺

Posted wuty007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了仿Word的支持横轴竖轴的WPF 标尺相关的知识,希望对你有一定的参考价值。

最近在  https://mp.weixin.qq.com/s/3dEO0NZQv5YLqK72atG4Wg   官方公众号看到了 用WPF 制作 标尺

 

在去年项目上也接到了一个需求,用于排版自定义拖拽控件画布对齐的标尺,当时接到的要求是 需要横纵对齐的表次,并且鼠标滑动,刻度的上方需要跟着有影子划过的效果。

 

具体实现如下:

创建 标尺控件 RulerControl.cs 

 [TemplatePart(Name = "trackLine", Type = typeof(Line))]
     internal class RulerControl : Control
     {
         public static readonly DependencyProperty DpiProperty = DependencyProperty.Register("Dpi", typeof(Dpi), typeof(RulerControl));
         public static readonly DependencyProperty DisplayPercentProperty = DependencyProperty.Register("DisplayPercent", typeof(double), typeof(RulerControl));
         public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register("DisplayType", typeof(RulerDisplayType), typeof(RulerControl));
         public static readonly DependencyProperty DisplayUnitProperty = DependencyProperty.Register("DisplayUnit", typeof(RulerDisplayUnit), typeof(RulerControl));
         public static readonly DependencyProperty ZeroPointProperty = DependencyProperty.Register("ZeroPoint", typeof(double), typeof(RulerControl));
 
         /// <summary>
         /// 定义静态构造函数
         /// </summary>
         static RulerControl()
         {
             DefaultStyleKeyProperty.OverrideMetadata(typeof(RulerControl), new FrameworkPropertyMetadata(typeof(RulerControl)));
         }
 
         #region 属性
         /// <summary>
         /// 屏幕分辨率
         /// </summary>
         public Dpi Dpi
         {
             get
             {
                 return ((Dpi)GetValue(DpiProperty));
             }
             set
             {
                 SetValue(DpiProperty, value);
             }
         }
 
         /// <summary>
         /// 设置0点从哪里开始
         /// </summary>
         public double ZeroPoint
         {
             get
             {
                 return ((double)GetValue(ZeroPointProperty));
             }
             set
             {
                 SetValue(ZeroPointProperty, value);
                 InvalidateVisual();
             }
         }
 
         /// <summary>
         /// 显示的比率(目前支持0-1的选项)
         /// </summary>
         public double DisplayPercent
         {
             get
             {
                 return ((double)GetValue(DisplayPercentProperty));
             }
             set
             {
                 if (value > 1)
                 {
                     value = 1;
                 }
                 SetValue(DisplayPercentProperty, value);
                 InvalidateVisual();
             }
         }
 
         /// <summary>
         /// 显示的类型:枚举类(支持横向或者竖向)
         /// </summary>
         public RulerDisplayType DisplayType
         {
             get
             {
                 return ((RulerDisplayType)GetValue(DisplayTypeProperty));
             }
             set
             {
                 SetValue(DisplayTypeProperty, value);
             }
         }
 
         /// <summary>
         /// 显示的单位:cm和pixel
         /// </summary>
         public RulerDisplayUnit DisplayUnit
         {
             get
             {
                 return ((RulerDisplayUnit)GetValue(DisplayUnitProperty));
             }
             set
             {
                 SetValue(DisplayUnitProperty, value);
             }
         }
         #endregion
 
         #region 常量
         public const double _inchCm = 2.54; //一英寸为2.54cm
         private const int _p100StepSpanPixel = 100;
         private const int _p100StepSpanCm = 2;
         private const int _p100StepCountPixel = 20;
         private const int _p100StepCountCm = 20;
 
         #endregion
 
         #region 变量
         private double _minStepLengthCm;
         private double _maxStepLengthCm;
         private double _actualLength;
         private int _stepSpan;
         private int _stepCount;
         private double _stepLength;
         Line mouseVerticalTrackLine;
         Line mouseHorizontalTrackLine;
         #endregion
 
         #region 标尺边框加指针显示 
         public void RaiseHorizontalRulerMoveEvent(MouseEventArgs e)
         {
             Point mousePoint = e.GetPosition(this);
             mouseHorizontalTrackLine.X1 = mouseHorizontalTrackLine.X2 = mousePoint.X;
         }
         public void RaiseVerticalRulerMoveEvent(MouseEventArgs e)
         {
             Point mousePoint = e.GetPosition(this);
             mouseVerticalTrackLine.Y1 = mouseVerticalTrackLine.Y2 = mousePoint.Y;
         }
 
         public override void OnApplyTemplate()
         {
             base.OnApplyTemplate();
             mouseVerticalTrackLine = GetTemplateChild("verticalTrackLine") as Line;
             mouseHorizontalTrackLine = GetTemplateChild("horizontalTrackLine") as Line;
             mouseVerticalTrackLine.Visibility = Visibility.Visible;
             mouseHorizontalTrackLine.Visibility = Visibility.Visible;
         }
         #endregion
 
         /// <summary>
         /// 重画标尺数据
         /// </summary>
         /// <param name="drawingContext"></param>
         protected override void OnRender(DrawingContext drawingContext)
         {
             try
             {
                 Pen pen = new Pen(new SolidColorBrush(Colors.Black),0.8d);
                 pen.Freeze();
                 Initialize();
                 GetActualLength();
                 GetStep();
                 base.OnRender(drawingContext);
 
                 this.BorderBrush = new SolidColorBrush(Colors.Black);
                 this.BorderThickness = new Thickness(0.1);
                 this.Background = new SolidColorBrush(Colors.White);
 
                 #region try
                 // double actualPx = this._actualLength / DisplayPercent;
                 Position currentPosition = new Position
                 {
                     CurrentStepIndex = 0,
                     Value = 0
                 };
 
                 switch (DisplayType)
                 {
                     case RulerDisplayType.Horizontal:
                         {
                             /* 绘制前半段 */
                             DrawLine(drawingContext, ZeroPoint, currentPosition, pen, 0);
                             /* 绘制后半段 */
                             DrawLine(drawingContext, ZeroPoint, currentPosition, pen, 1);
                             break;
                         }
                     case RulerDisplayType.Vertical:
                         {
                             /* 绘制前半段 */
                             DrawLine(drawingContext, ZeroPoint, currentPosition, pen, 0);
                             /* 绘制后半段 */
                             DrawLine(drawingContext, ZeroPoint, currentPosition, pen, 1);
                             break;
                         }
                 }
                 #endregion
             }
             catch (Exception ex)
             {
                 Console.WriteLine(ex.Message);
             }
         }
 
         private void DrawLine(DrawingContext drawingContext, double currentPoint, Position currentPosition, Pen pen, int type)
         {
             double linePercent = 0d;
             while (true)
             {
                 if (currentPosition.CurrentStepIndex == 0)
                 {
             
                     FormattedText formattedText = GetFormattedText((currentPosition.Value / 10).ToString());
 
                     
                     switch (DisplayType)
                     {
                         case RulerDisplayType.Horizontal:
                             {
                                 var point = new Point(currentPoint + formattedText.Width / 2, formattedText.Height / 3);
                                 if (point.X<0)
                                 {
                                     break;
                                 }
                                 drawingContext.DrawText(formattedText, point);
                                 break;
                             }
                         case RulerDisplayType.Vertical:
                             {       
                                 Point point = new Point(this.ActualWidth, currentPoint + formattedText.Height / 2);
                                 RotateTransform rotateTransform = new RotateTransform(90, point.X, point.Y);
                                 if (point.Y<0)
                                 {
                                     break;
                                 }
                                 drawingContext.PushTransform(rotateTransform);
                                 drawingContext.DrawText(formattedText, point);
                                 drawingContext.Pop();
                                 break;
                             }
                     }
 
                     linePercent = (int)LinePercent.P100;
                 }
                 else if (IsFinalNum(currentPosition.CurrentStepIndex, 3))
                 {
                     linePercent = (int)LinePercent.P30;
                 }
                 else if (IsFinalNum(currentPosition.CurrentStepIndex, 5))
                 {
                     linePercent = (int)LinePercent.P50;
                 }
                 else if (IsFinalNum(currentPosition.CurrentStepIndex, 7))
                 {
                     linePercent = (int)LinePercent.P30;
                 }
                 else if (IsFinalNum(currentPosition.CurrentStepIndex, 0))
                 {
                     linePercent = (int)LinePercent.P70;
                 }
                 else
                 {
                     linePercent = (int)LinePercent.P20;
                 }
 
                 linePercent = linePercent * 0.01;        
 
                 switch (DisplayType)
                 {
                     case RulerDisplayType.Horizontal:
                         {
                             if (currentPoint > 0)
                             {
                                 drawingContext.DrawLine(pen, new Point(currentPoint, 0), new Point(currentPoint, this.ActualHeight * linePercent));
                             }
 
                             if (type == 0)
                             {
                                 currentPoint = currentPoint - _stepLength;
                                 currentPosition.CurrentStepIndex--;
 
                                 if (currentPosition.CurrentStepIndex < 0)
                                 {
                                     currentPosition.CurrentStepIndex = _stepCount - 1;
                                     currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 0);
                                 }
                                 else if (currentPosition.CurrentStepIndex == 0)
                                 {
                                     if (currentPosition.Value % _stepSpan != 0)
                                     {
                                         currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 0);
                                     }
                                 }
 
                                 if (currentPoint <= 0)
                                 {
                                     return;
                                 }
                             }
                             else
                             {
                                 currentPoint = currentPoint + _stepLength;
                                 currentPosition.CurrentStepIndex++;
 
                                 if (currentPosition.CurrentStepIndex >= _stepCount)
                                 {
                                     currentPosition.CurrentStepIndex = 0;
                                     currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 1);
                                 }
 
                                 if (currentPoint >= _actualLength)
                                 {
                                     return;
                                 }
                             }
                             break;
                         }
                     case RulerDisplayType.Vertical:
                         {
                             if (currentPoint > 0)
                             {
                                 drawingContext.DrawLine(pen, new Point(0, currentPoint), new Point(this.ActualWidth * linePercent, currentPoint));
                             }
                             if (type == 0)
                             {
                                 currentPoint = currentPoint - _stepLength;
                                 currentPosition.CurrentStepIndex--;
 
                                 if (currentPosition.CurrentStepIndex < 0)
                                 {
                                     currentPosition.CurrentStepIndex = _stepCount - 1;
                                     currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 0);
                                 }
                                 else if (currentPosition.CurrentStepIndex == 0)
                                 {
                                     if (currentPosition.Value % _stepSpan != 0)
                                     {
                                         currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 0);
                                     }
                                 }
 
                                 if (currentPoint <= 0)
                                 {
                                     return;
                                 }
                             }
                             else
                             {
                                 currentPoint = currentPoint + _stepLength;
                                 currentPosition.CurrentStepIndex++;
 
                                 if (currentPosition.CurrentStepIndex >= _stepCount)
                                 {
                                     currentPosition.CurrentStepIndex = 0;
                                     currentPosition.Value = GetNextStepValue(currentPosition.Value, _stepSpan, 1);
                                 }
 
                                 if (currentPoint >= _actualLength)
                                 {
                                     return;
                                 }
                             }
                             break;
                         }
                 }
             }
         }
 
         /// <summary>
         /// 获取下一个步长值
         /// </summary>
         /// <param name="value">起始值</param>
         /// <param name="times">跨度</param>
         /// <param name="type">半段类型,分为前半段、后半段</param>
         /// <returns></returns>
         private int GetNextStepValue(int value, int times, int type)
         {
             if (type == 0)
             {
                 do
                 {
                     value--;
                 }
                 while (value % times != 0);
             }
             else
             {
                 do
                 {
                     value++;
                 }
                 while (value % times != 0);
             }
             return (value);
         }
 
         [Obsolete]
         private FormattedText GetFormattedText(string text)
         {
             return (new FormattedText(text,
                           //CultureInfo.GetCultureInfo("zh-cn"),
                           CultureInfo.GetCultureInfo("en-us"),
                           FlowDirection.LeftToRight,
                           new Typeface("宋体"),
                           12,
                           Brushes.Black));
         }
 
         private bool IsFinalNum(int value, int finalNum)
         {
             string valueStr = value.ToString();
             if (valueStr.Substring(valueStr.Length - 1, 1) == finalNum.ToString())
             {
                 return (true);
             }
             return (false);
         }
 
         /// <summary>
         /// 初始化获取屏幕的DPI
         /// </summary>
         private void Initialize()
         {
             Dpi dpi = new Dpi();
             dpi.DpiX = Dpi.DpiX;
             dpi.DpiY = Dpi.DpiY;
             if (Dpi.DpiX == 0)
             {
                 dpi.DpiX = 96;
             }
 
             if (Dpi.DpiY == 0)
             {
                 dpi.DpiY = 96;
             }
 
             Dpi = dpi;
             _minStepLengthCm = 0.1;
             _maxStepLengthCm = 0.3;
 
             if (DisplayPercent == 0)
                 DisplayPercent = 1;
 
             switch (DisplayUnit)
             {
                 case RulerDisplayUnit.pixel:
                     {
                         _stepSpan = _p100StepSpanPixel;
                         _stepCount = _p100StepCountPixel;
                         break;
                     }
                 case RulerDisplayUnit.cm:
                     {
                         _stepSpan = _p100StepSpanCm;
                         _stepCount = _p100StepCountCm;
                         break;
                     }
             }
             int width = 15;
             switch (DisplayType)
             {
                 case RulerDisplayType.Horizontal:
                     {
                         if (this.ActualHeight == 0)
                         {
                             Height = width;
                         }
                         break;
                     }
                 case RulerDisplayType.Vertical:
                     {
                         if (this.ActualWidth == 0)
                         {
                             Width = width;
                         }
                         break;
                     }
             }
         }
 
         /// <summary>
         /// 获取每一个数字间隔的跨度
         /// </summary>
         private void GetStep()
         {
             switch (DisplayUnit)
             {
                 case RulerDisplayUnit.pixel:
                     {
                         double stepSpanCm;
                         while (true)
                         {  
                             stepSpanCm = _stepSpan / Convert.ToDouble(GetDpi()) * _inchCm * DisplayPercent;
                             double stepLengthCm = stepSpanCm / _stepCount;
                             int type = 0;
                             bool isOut = false;
                             if (stepLengthCm > _maxStepLengthCm)
                             {
                                 type = 1;
                                 _stepCount = GetNextStepCount(_stepCount, type, ref isOut);
                             }
 
                             if (stepLengthCm < _minStepLengthCm)
                             {
                                 type = 0;
                                 _stepCount = GetNextStepCount(_stepCount, type, ref isOut);
                             }
 
                             if (stepLengthCm <= _maxStepLengthCm && stepLengthCm >= _minStepLengthCm)
                             {
                                 _stepLength = stepSpanCm / _inchCm * Convert.ToDouble(GetDpi()) / _stepCount;
                                 break;
                             }
                             /* 已超出或小于最大步进长度 */
                             if (isOut)
                             {
                                 _stepSpan = GetNextStepSpan(_stepSpan, type);
                                 continue;
                             }
                         }
                         break;
                     }
             }
         }
 
        
         private int GetNextStepCount(int stepCount, int type, ref bool isOut)
         {
             int result = stepCount;
             isOut = false;
             switch (type)
             {
                 case 0:
                     {
                         if (stepCount == 20)
                         {
                             result = 10;
                         }
                         else
                         {
                             isOut = true;
                         }
                         break;
                     }
                 case 1:
                     {
                         if (stepCount == 10)
                         {
                             result = 20;
                         }
                         else
                         {
                             isOut = true;
                         }
 
                         break;
                     }
             }
             return result;
         }
 
 
         private int GetNextStepSpan(int stepSpan, int type)
         {
             怎样将word标尺调出来?word标尺的使用技巧!

wpf仿qq边缘自动停靠,支持多屏

wpf自定义标尺

Word标尺与段落

WPF实现雷达图(仿英雄联盟)

word中的制表符?