WPF游标直尺
效果图:

使用方法:
<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));
}
}
}
浙公网安备 33010602011771号