X3

RedSky

导航

WPF游标直尺

效果图:

屏幕截图 2026-07-31 172811

使用方法:

<controls:Ruler x:Name="rulerV" Background="White" Foreground="Black" UnitType="Px" Dpi="72" Orientation="Vertical" Maximum="{Binding ElementName=paintAreaCanvas, Path=ActualHeight}" Scale="{Binding ElementName=scaleBehavior, Path=Scale}" ReservedSpace="{Binding ElementName=scrollviewer, Path=VerticalOffset}" SnapsToDevicePixels="True"/>

代码:

    public enum UnitType { Px, MM }
    public class Ruler : FrameworkElement
    {
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Ruler), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsRender));

        public UnitType UnitType
        {
            get { return (UnitType)GetValue(UnitTypeProperty); }
            set { SetValue(UnitTypeProperty, value); }
        }

        public static readonly DependencyProperty UnitTypeProperty =
            DependencyProperty.Register("UnitType", typeof(UnitType), typeof(Ruler), new FrameworkPropertyMetadata(UnitType.Px, FrameworkPropertyMetadataOptions.AffectsRender));

        public double Scale
        {
            get { return (double)GetValue(ScaleProperty); }
            set { SetValue(ScaleProperty, value); }
        }

        public static readonly DependencyProperty ScaleProperty =
            DependencyProperty.Register("Scale", typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));

        public double ReservedSpace
        {
            get { return (double)GetValue(ReservedSpaceProperty); }
            set { SetValue(ReservedSpaceProperty, value); }
        }

        public static readonly DependencyProperty ReservedSpaceProperty =
            DependencyProperty.Register("ReservedSpace", typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));

        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsRender));

        public double TickFrequency
        {
            get { return (double)GetValue(TickFrequencyProperty); }
            set { SetValue(TickFrequencyProperty, value); }
        }

        public static readonly DependencyProperty TickFrequencyProperty =
            DependencyProperty.Register("TickFrequency", typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));



        public double Dpi
        {
            get { return (double)GetValue(DpiProperty); }
            set { SetValue(DpiProperty, value); }
        }

        public static readonly DependencyProperty DpiProperty =
            DependencyProperty.Register("Dpi", typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(96.0, FrameworkPropertyMetadataOptions.AffectsRender));



        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register("Background", typeof(Brush), typeof(Ruler), new FrameworkPropertyMetadata(Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender));



        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }

        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.Register("Foreground", typeof(Brush), typeof(Ruler), new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));



        public Ruler()
        {
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            if(Orientation == Orientation.Horizontal)
                RenderHorizon(drawingContext);
            else
                RenderVertical(drawingContext);
        }

        void RenderHorizon(DrawingContext drawingContext)
        {
            if (double.IsNaN(Maximum)) return;
            drawingContext.PushGuidelineSet(new GuidelineSet(new double[] { 0, 0.5 }, new double[] { 0, 0.5, ActualHeight / 2.0, ActualHeight * 3.0 / 4.0 }));
            Pen pen = new Pen(Foreground, 1);

            double max = Math.Min(Maximum, ReservedSpace + ActualWidth);
            double unit0 = 0;
            double scales = 1;
            switch (UnitType)
            {
                case UnitType.Px:
                    unit0 = 10.0;
                    if (Scale >= 5)
                        scales = 0.1;
                    else if (Scale >= 4)
                        scales = 0.2;
                    else if (Scale >= 3)
                        scales = 0.4;
                    else if (Scale >= 2)
                        scales = 0.5;
                    break;
                case UnitType.MM:
                    unit0 = 2.0;
                    if (Scale >= 14)
                        scales = 0.05;
                    else if(Scale >= 8)
                        scales = 0.1;
                    else if (Scale >= 5)
                        scales = 0.2;
                    else if (Scale >= 4)
                        scales = 0.3;
                    else if (Scale >= 3)
                        scales = 0.4;
                    else if (Scale >= 2)
                        scales = 0.5;
                    break;
            }
            double unit = (UnitType == UnitType.MM ? unit0.ToPx((float)Dpi) : unit0) * scales * Scale;
            double startValue = ReservedSpace;
            double offset = startValue % unit;
            int c = (int)Math.Floor(startValue / unit);
            double current = startValue;
            drawingContext.DrawLine(pen, new Point(2, 0), new Point(2, ActualHeight));
            current += unit - offset;
            while (current < max)
            {
                c++;
                bool isHalf = c % 5 == 0;
                bool isFull = c % 10 == 0;
                double x = current - startValue;
                double y1 = isFull ? 0.0 : (isHalf ? ActualHeight / 2.0 : ActualHeight * 3 / 4.0);
                double y2 = ActualHeight;
                drawingContext.DrawLine(pen, new Point(x + 2, y1), new Point(x + 2, y2));

                if (isFull)
                {
                    drawingContext.DrawText(new FormattedText((c * unit0 * scales).ToString(),CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("sim"), 12, Foreground, 1), new Point(x + 2, 0));
                }
                current += unit;
            }

            if (current >= max)
            {
                current = max;
                drawingContext.DrawLine(pen, new Point(current - startValue, 0), new Point(current - startValue, ActualHeight));
            }
        }

        void RenderVertical(DrawingContext drawingContext)
        {
            if (Maximum == double.NaN) return;
            drawingContext.PushGuidelineSet(new GuidelineSet(new double[] { 0, 0.5, ActualWidth / 2.0, ActualWidth * 3.0 / 4.0, ActualWidth }, new double[] { 0, 0.5 }));
            Pen pen = new Pen(Foreground, 1);

            double max = Math.Min(Maximum, ReservedSpace + ActualHeight);
            double unit0 = 0;
            double scales = 1;
            switch (UnitType)
            {
                case UnitType.Px:
                    unit0 = 10.0;
                    if (Scale >= 5)
                        scales = 0.1;
                    else if (Scale >= 4)
                        scales = 0.2;
                    else if (Scale >= 3)
                        scales = 0.4;
                    else if (Scale >= 2)
                        scales = 0.5;
                    break;
                case UnitType.MM:
                    unit0 = 2.0;
                    if (Scale >= 6)
                        scales = 0.05;
                    else if (Scale >= 5)
                        scales = 0.1;
                    else if (Scale >= 4)
                        scales = 0.2;
                    else if (Scale >= 3)
                        scales = 0.4;
                    else if (Scale >= 2)
                        scales = 0.5;
                    break;
            }
            double unit = (UnitType == UnitType.MM ? unit0.ToPx((float)Dpi) : unit0) * scales * Scale;
            double startValue = ReservedSpace;
            double offset = startValue % unit;
            int c = (int)Math.Floor(startValue / unit);
            double current = startValue;
            drawingContext.DrawLine(pen, new Point(0, 2), new Point(ActualWidth, 2));
            current += unit - offset;
            while (current < max)
            {
                c++;
                bool isHalf = c % 5 == 0;
                bool isFull = c % 10 == 0;
                double y = current - startValue;
                double x1 = isFull ? 0.0 : (isHalf ? ActualWidth / 2.0 : ActualWidth * 3 / 4.0);
                double x2 = ActualWidth;
                drawingContext.DrawLine(pen, new Point(x1, y + 2), new Point(x2, y + 2));

                if (isFull)
                {
                    drawingContext.PushTransform(new RotateTransform(90, 0, y));
                    drawingContext.DrawText(new FormattedText((c * unit0 * scales).ToString(), CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("sim"), 12, Foreground, 1), new Point(4, y - 12));
                    drawingContext.Pop();
                }
                current += unit;
            }

            if(current >= max)
            {
                current = max;
                drawingContext.DrawLine(pen, new Point(0, current - startValue), new Point(ActualWidth, current - startValue));
            }
        }
    }

 

posted on 2026-07-31 17:34  HotSky  阅读(1)  评论(0)    收藏  举报