using System;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Controls
{
///
/// A ruler control which supports both centimeters and inches. In order to use it vertically, change the Marks property to Up and rotate it ninety degrees.
///
///
/// Contributions from Raf
/// Lenfers
///
public class Ruler : FrameworkElement
{
#region Fields
private double SegmentHeight;
private readonly Pen p = new Pen(Brushes.Black, 1.0);
private readonly Pen ThinPen = new Pen(Brushes.Black, 0.5);
private readonly Pen BorderPen = new Pen(Brushes.Gray, 1.0);
private readonly Pen RedPen = new Pen(Brushes.Red, 2.0);
#endregion
#region Properties
#region Background
public Brush Background
{
get
{
return (Brush)GetValue(BackgroundProperty);
}
set
{
SetValue(BackgroundProperty, value);
}
}
///
/// Identifies the Length dependency property.
///
public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.Register(
"Background",
typeof(Brush),
typeof(Ruler),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.White), FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region Length
///
/// Gets or sets the length of the ruler. If the property is set to false (default) this
/// is a fixed length. Otherwise the length is calculated based on the actual width of the ruler.
///
public double Length
{
get
{
if (this.AutoSize)
{
return (double)(Unit == Unit.Cm ? ScreenHelper.WidthToCm(this.ActualWidth) : ScreenHelper.WidthToInch(this.ActualWidth)) / this.Zoom;
}
else
{
return (double)GetValue(LengthProperty);
}
}
set
{
SetValue(LengthProperty, value);
}
}
///
/// Identifies the Length dependency property.
///
public static readonly DependencyProperty LengthProperty =
DependencyProperty.Register(
"Length",
typeof(double),
typeof(Ruler),
new FrameworkPropertyMetadata(20D, FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region AutoSize
///
/// Gets or sets the AutoSize behavior of the ruler.
/// false (default): the lenght of the ruler results from the property. If the window size is changed, e.g. wider
/// than the rulers length, free space is shown at the end of the ruler. No rescaling is done.
/// true : the length of the ruler is always adjusted to its actual width. This ensures that the ruler is shown
/// for the actual width of the window.
///
public bool AutoSize
{
get
{
return (bool)GetValue(AutoSizeProperty);
}
set
{
SetValue(AutoSizeProperty, value);
this.InvalidateVisual();
}
}
///
/// Identifies the AutoSize dependency property.
///
public static readonly DependencyProperty AutoSizeProperty =
DependencyProperty.Register(
"AutoSize",
typeof(bool),
typeof(Ruler),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region Zoom
///
/// Gets or sets the zoom factor for the ruler. The default value is 1.0.
///
public double Zoom
{
get
{
return (double)GetValue(ZoomProperty);
}
set
{
SetValue(ZoomProperty, value);
this.InvalidateVisual();
}
}
///
/// Identifies the Zoom dependency property.
///
public static readonly DependencyProperty ZoomProperty =
DependencyProperty.Register("Zoom", typeof(double), typeof(Ruler),
new FrameworkPropertyMetadata((double)1.0,
FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region Chip
///
/// Chip Dependency Property
///
public static readonly DependencyProperty ChipProperty =
DependencyProperty.Register("Chip", typeof(double), typeof(Ruler),
new FrameworkPropertyMetadata((double)-1000,
FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Sets the location of the chip in the units of the ruler.
/// So, to set the chip to 10 in cm units the chip needs to be set to 10.
/// Use the class for conversions.
///
public double Chip
{
get { return (double)GetValue(ChipProperty); }
set { SetValue(ChipProperty, value); }
}
#endregion
#region CountShift
///
/// CountShift Dependency Property
///
public static readonly DependencyProperty CountShiftProperty =
DependencyProperty.Register("CountShift", typeof(double), typeof(Ruler),
new FrameworkPropertyMetadata(0d,
FrameworkPropertyMetadataOptions.AffectsRender));
///
/// By default the counting of inches or cm starts at zero, this property allows you to shift
/// the counting.
///
public double CountShift
{
get { return (double)GetValue(CountShiftProperty); }
set { SetValue(CountShiftProperty, value); }
}
#endregion
#region Marks
///
/// Marks Dependency Property
///
public static readonly DependencyProperty MarksProperty =
DependencyProperty.Register("Marks", typeof(MarksLocation), typeof(Ruler),
new FrameworkPropertyMetadata(MarksLocation.Up,
FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets where the marks are shown in the ruler.
///
public MarksLocation Marks
{
get { return (MarksLocation)GetValue(MarksProperty); }
set { SetValue(MarksProperty, value); }
}
#endregion
#region Unit
///
/// Gets or sets the unit of the ruler.
/// Default value is Unit.Cm.
///
public Unit Unit
{
get { return (Unit)GetValue(UnitProperty); }
set { SetValue(UnitProperty, value); }
}
///
/// Identifies the Unit dependency property.
///
public static readonly DependencyProperty UnitProperty =
DependencyProperty.Register(
"Unit",
typeof(Unit),
typeof(Ruler),
new FrameworkPropertyMetadata(Unit.Cm, FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#endregion
#region Constructor
static Ruler()
{
HeightProperty.OverrideMetadata(typeof(Ruler), new FrameworkPropertyMetadata(20.0));
}
public Ruler()
{
SegmentHeight = this.Height - 10;
}
#endregion
#region Methods
///
/// Participates in rendering operations.
///
/// The drawing instructions for a specific element. This context is provided to the layout system.
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
drawingContext.DrawRectangle(Background, null, rect);
double xDest = (Unit == Unit.Cm ? ScreenHelper.CmToWidth(Length) : ScreenHelper.InchToWidth(Length)) * this.Zoom;
drawingContext.DrawRectangle(null, BorderPen, new Rect(new Point(0.0, 0.0), new Point(xDest, Height)));
double chip = Unit == Unit.Cm ? ScreenHelper.CmToWidth(Chip) : ScreenHelper.InchToWidth(Chip);
drawingContext.DrawLine(RedPen, new Point(chip, 0), new Point(chip, Height));
//画偏移位置之前的
if (CountShift < 0)
{
for (double dUnit = -CountShift; dUnit > -1; dUnit--)
{
double d;
if (Unit == Unit.Cm)
{
d = ScreenHelper.CmToWidth(dUnit) * this.Zoom;
if (dUnit < Length)
{
for (int i = 1; i <= 9; i++)
{
if (i != 5)
{
double dMm = ScreenHelper.CmToWidth(dUnit + 0.1 * i) * this.Zoom;
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(ThinPen, new Point(dMm, 0), new Point(dMm, SegmentHeight / 3.0));
else
drawingContext.DrawLine(ThinPen, new Point(dMm, Height), new Point(dMm, Height - SegmentHeight / 3.0));
}
}
double dMiddle = ScreenHelper.CmToWidth(dUnit + 0.5) * this.Zoom;
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(p, new Point(dMiddle, 0), new Point(dMiddle, SegmentHeight * 2.0 / 3.0));
else
drawingContext.DrawLine(p, new Point(dMiddle, Height), new Point(dMiddle, Height - SegmentHeight * 2.0 / 3.0));
}
}
else
{
d = ScreenHelper.InchToWidth(dUnit) * this.Zoom;
if (dUnit < Length)
{
if (Marks == MarksLocation.Up)
{
double dQuarter = ScreenHelper.InchToWidth(dUnit + 0.25) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(dQuarter, 0),
new Point(dQuarter, SegmentHeight / 3.0));
double dMiddle = ScreenHelper.InchToWidth(dUnit + 0.5) * this.Zoom;
drawingContext.DrawLine(p, new Point(dMiddle, 0),
new Point(dMiddle, SegmentHeight * 2D / 3D));
double d3Quarter = ScreenHelper.InchToWidth(dUnit + 0.75) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(d3Quarter, 0),
new Point(d3Quarter, SegmentHeight / 3.0));
}
else
{
double dQuarter = ScreenHelper.InchToWidth(dUnit + 0.25) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(dQuarter, Height),
new Point(dQuarter, Height - SegmentHeight / 3.0));
double dMiddle = ScreenHelper.InchToWidth(dUnit + 0.5) * this.Zoom;
drawingContext.DrawLine(p, new Point(dMiddle, Height),
new Point(dMiddle, Height - SegmentHeight * 2D / 3D));
double d3Quarter = ScreenHelper.InchToWidth(dUnit + 0.75) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(d3Quarter, Height),
new Point(d3Quarter, Height - SegmentHeight / 3.0));
}
}
}
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(p, new Point(d, 0), new Point(d, SegmentHeight));
else
drawingContext.DrawLine(p, new Point(d, Height), new Point(d, Height - SegmentHeight));
if ((dUnit != 0.0) && (dUnit < Length))
{
FormattedText ft = new FormattedText(
(Math.Round(dUnit + CountShift,0)).ToString(CultureInfo.CurrentCulture),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Arial"),
9,
Brushes.DimGray);
ft.SetFontWeight(FontWeights.Regular);
ft.TextAlignment = TextAlignment.Center;
if (Marks == MarksLocation.Up)
drawingContext.DrawText(ft, new Point(d, Height - ft.Height));
else
drawingContext.DrawText(ft, new Point(d, Height - SegmentHeight - ft.Height));
}
}
}
//画偏移位置之后的
for (double dUnit = -CountShift; dUnit <= Length; dUnit++)
{
double d;
if (Unit == Unit.Cm)
{
d = ScreenHelper.CmToWidth(dUnit) * this.Zoom;
if (dUnit < Length)
{
for (int i = 1; i <= 9; i++)
{
if (i != 5)
{
double dMm = ScreenHelper.CmToWidth(dUnit + 0.1 * i) * this.Zoom;
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(ThinPen, new Point(dMm, 0), new Point(dMm, SegmentHeight / 3.0));
else
drawingContext.DrawLine(ThinPen, new Point(dMm, Height), new Point(dMm, Height - SegmentHeight / 3.0));
}
}
double dMiddle = ScreenHelper.CmToWidth(dUnit + 0.5) * this.Zoom;
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(p, new Point(dMiddle, 0), new Point(dMiddle, SegmentHeight * 2.0 / 3.0));
else
drawingContext.DrawLine(p, new Point(dMiddle, Height), new Point(dMiddle, Height - SegmentHeight * 2.0 / 3.0));
}
}
else
{
d = ScreenHelper.InchToWidth(dUnit) * this.Zoom;
if (dUnit < Length)
{
if (Marks == MarksLocation.Up)
{
double dQuarter = ScreenHelper.InchToWidth(dUnit + 0.25) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(dQuarter, 0),
new Point(dQuarter, SegmentHeight / 3.0));
double dMiddle = ScreenHelper.InchToWidth(dUnit + 0.5) * this.Zoom;
drawingContext.DrawLine(p, new Point(dMiddle, 0),
new Point(dMiddle, SegmentHeight * 2D / 3D));
double d3Quarter = ScreenHelper.InchToWidth(dUnit + 0.75) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(d3Quarter, 0),
new Point(d3Quarter, SegmentHeight / 3.0));
}
else
{
double dQuarter = ScreenHelper.InchToWidth(dUnit + 0.25) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(dQuarter, Height),
new Point(dQuarter, Height - SegmentHeight / 3.0));
double dMiddle = ScreenHelper.InchToWidth(dUnit + 0.5) * this.Zoom;
drawingContext.DrawLine(p, new Point(dMiddle, Height),
new Point(dMiddle, Height - SegmentHeight * 2D / 3D));
double d3Quarter = ScreenHelper.InchToWidth(dUnit + 0.75) * this.Zoom;
drawingContext.DrawLine(ThinPen, new Point(d3Quarter, Height),
new Point(d3Quarter, Height - SegmentHeight / 3.0));
}
}
}
if (Marks == MarksLocation.Up)
drawingContext.DrawLine(p, new Point(d, 0), new Point(d, SegmentHeight));
else
drawingContext.DrawLine(p, new Point(d, Height), new Point(d, Height - SegmentHeight));
if ((dUnit != 0.0) && (dUnit < Length))
{
FormattedText ft = new FormattedText(
(Math.Round(dUnit + CountShift,0)).ToString(CultureInfo.CurrentCulture),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Arial"),
9,
Brushes.DimGray);
ft.SetFontWeight(FontWeights.Regular);
ft.TextAlignment = TextAlignment.Center;
if (Marks == MarksLocation.Up)
drawingContext.DrawText(ft, new Point(d, Height - ft.Height));
else
drawingContext.DrawText(ft, new Point(d, Height - SegmentHeight - ft.Height));
}
}
}
///
/// Measures an instance during the first layout pass prior to arranging it.
///
/// A maximum Size to not exceed.
/// The maximum Size for the instance.
protected override Size MeasureOverride(Size availableSize)
{
return base.MeasureOverride(availableSize);
//Size desiredSize;
//if (Unit == Unit.Cm)
//{
// desiredSize = new Size(ScreenHelper.CmToWidth(Length), Height);
//}
//else
//{
// desiredSize = new Size(ScreenHelper.InchToWidth(Length), Height);
//}
//return desiredSize;
}
#endregion
}
///
/// The unit type of the ruler.
///
public enum Unit
{
///
/// the unit is Centimeter.
///
Cm,
///
/// The unit is Inch.
///
Inch
};
public enum MarksLocation
{
Up, Down
}
/////
///// A helper class for DIP (Device Independent Pixels) conversion and scaling operations.
/////
//public static class DipHelper
//{
// ///
// /// Converts millimeters to DIP (Device Independant Pixels).
// ///
// /// A millimeter value.
// /// A DIP value.
// public static double MmToDip(double mm)
// {
// return CmToDip(mm / 10.0);
// }
// ///
// /// Converts centimeters to DIP (Device Independant Pixels).
// ///
// /// A centimeter value.
// /// A DIP value.
// public static double CmToDip(double cm)
// {
// return (cm * 96.0 / 2.54);
// }
// ///
// /// Converts inches to DIP (Device Independant Pixels).
// ///
// /// An inch value.
// /// A DIP value.
// public static double InchToDip(double inch)
// {
// return (inch * 96.0);
// }
// public static double DipToInch(double dip)
// {
// return dip / 96D;
// }
// ///
// /// Converts font points to DIP (Device Independant Pixels).
// ///
// /// A font point value.
// /// A DIP value.
// public static double PtToDip(double pt)
// {
// return (pt * 96.0 / 72.0);
// }
// ///
// /// Converts DIP (Device Independant Pixels) to centimeters.
// ///
// /// A DIP value.
// /// A centimeter value.
// public static double DipToCm(double dip)
// {
// return (dip * 2.54 / 96.0);
// }
// ///
// /// Converts DIP (Device Independant Pixels) to millimeters.
// ///
// /// A DIP value.
// /// A millimeter value.
// public static double DipToMm(double dip)
// {
// return DipToCm(dip) * 10.0;
// }
// ///
// /// Gets the system DPI scale factor (compared to 96 dpi).
// /// From http://blogs.msdn.com/jaimer/archive/2007/03/07/getting-system-dpi-in-wpf-app.aspx
// /// Should not be called before the Loaded event (else XamlException mat throw)
// ///
// /// A Point object containing the X- and Y- scale factor.
// private static Point GetSystemDpiFactor()
// {
// PresentationSource source = PresentationSource.FromVisual(Application.Current.MainWindow);
// Matrix m = source.CompositionTarget.TransformToDevice;
// return new Point(m.M11, m.M22);
// }
// private const double DpiBase = 96.0;
// ///
// /// Gets the system configured DPI.
// ///
// /// A Point object containing the X- and Y- DPI.
// public static Point GetSystemDpi()
// {
// Point sysDpiFactor = GetSystemDpiFactor();
// return new Point(
// sysDpiFactor.X * DpiBase,
// sysDpiFactor.Y * DpiBase);
// }
// ///
// /// Gets the physical pixel density (DPI) of the screen.
// ///
// /// Size - in inch - of the diagonal of the screen.
// /// A Point object containing the X- and Y- DPI.
// public static Point GetPhysicalDpi(double diagonalScreenSize)
// {
// Point sysDpiFactor = GetSystemDpiFactor();
// double pixelScreenWidth = SystemParameters.PrimaryScreenWidth * sysDpiFactor.X;
// double pixelScreenHeight = SystemParameters.PrimaryScreenHeight * sysDpiFactor.Y;
// double formatRate = pixelScreenWidth / pixelScreenHeight;
// double inchHeight = diagonalScreenSize / Math.Sqrt(formatRate * formatRate + 1.0);
// double inchWidth = formatRate * inchHeight;
// double xDpi = Math.Round(pixelScreenWidth / inchWidth);
// double yDpi = Math.Round(pixelScreenHeight / inchHeight);
// return new Point(xDpi, yDpi);
// }
// ///
// /// Converts a DPI into a scale factor (compared to system DPI).
// ///
// /// A Point object containing the X- and Y- DPI to convert.
// /// A Point object containing the X- and Y- scale factor.
// public static Point DpiToScaleFactor(Point dpi)
// {
// Point sysDpi = GetSystemDpi();
// return new Point(
// dpi.X / sysDpi.X,
// dpi.Y / sysDpi.Y);
// }
// ///
// /// Gets the scale factor to apply to a WPF application
// /// so that 96 DIP always equals 1 inch on the screen (whatever the system DPI).
// ///
// /// Size - in inch - of the diagonal of the screen
// /// A Point object containing the X- and Y- scale factor.
// public static Point GetScreenIndependentScaleFactor(double diagonalScreenSize)
// {
// return DpiToScaleFactor(GetPhysicalDpi(diagonalScreenSize));
// }
//}
}