2 changed files with 610 additions and 17 deletions
@ -0,0 +1,593 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Layout; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Enum which describes how to position the TickBar.
|
||||
|
/// </summary>
|
||||
|
public enum TickBarPlacement |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Position this tick at the left of target element.
|
||||
|
/// </summary>
|
||||
|
Left, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Position this tick at the top of target element.
|
||||
|
/// </summary>
|
||||
|
Top, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Position this tick at the right of target element.
|
||||
|
/// </summary>
|
||||
|
Right, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Position this tick at the bottom of target element.
|
||||
|
/// </summary>
|
||||
|
Bottom, |
||||
|
|
||||
|
// NOTE: if you add or remove any values in this enum, be sure to update TickBar.IsValidTickBarPlacement()
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TickBar is an element that use for drawing Slider's Ticks.
|
||||
|
/// </summary>
|
||||
|
public class TickBar : TemplatedControl |
||||
|
{ |
||||
|
static TickBar() |
||||
|
{ |
||||
|
AffectsRender<TickBar>(ReservedSpaceProperty, MaximumProperty, MinimumProperty, OrientationProperty, TickFrequencyProperty); |
||||
|
} |
||||
|
|
||||
|
public TickBar() : base() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Fill"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IBrush> FillProperty = |
||||
|
AvaloniaProperty.Register<TickBar, IBrush>(nameof(Fill)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Brush used to fill the TickBar's Ticks.
|
||||
|
/// </summary>
|
||||
|
public IBrush Fill |
||||
|
{ |
||||
|
get { return GetValue(FillProperty); } |
||||
|
set { SetValue(FillProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Minimum"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> MinimumProperty = |
||||
|
AvaloniaProperty.Register<TickBar, double>(nameof(Minimum), 0d); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logical position where the Minimum Tick will be drawn
|
||||
|
/// </summary>
|
||||
|
public double Minimum |
||||
|
{ |
||||
|
get { return GetValue(MinimumProperty); } |
||||
|
set { SetValue(MinimumProperty, value); } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Maximum"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> MaximumProperty = |
||||
|
AvaloniaProperty.Register<TickBar, double>(nameof(Maximum), 0d); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logical position where the Maximum Tick will be drawn
|
||||
|
/// </summary>
|
||||
|
public double Maximum |
||||
|
{ |
||||
|
get { return GetValue(MaximumProperty); } |
||||
|
set { SetValue(MaximumProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="SelectionStart"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> SelectionStartProperty = |
||||
|
AvaloniaProperty.Register<TickBar, double>(nameof(SelectionStart), 0d); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logical position where the SelectionStart Tick will be drawn
|
||||
|
/// </summary>
|
||||
|
public double SelectionStart |
||||
|
{ |
||||
|
get { return GetValue(SelectionStartProperty); } |
||||
|
set { SetValue(SelectionStartProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="SelectionEnd"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> SelectionEndProperty = |
||||
|
AvaloniaProperty.Register<TickBar, double>(nameof(SelectionEnd), 0d); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logical position where the SelectionEnd Tick will be drawn
|
||||
|
/// </summary>
|
||||
|
public double SelectionEnd |
||||
|
{ |
||||
|
get { return GetValue(SelectionEndProperty); } |
||||
|
set { SetValue(SelectionEndProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="IsSelectionRangeEnabled"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> IsSelectionRangeEnabledProperty = |
||||
|
AvaloniaProperty.Register<TickBar, bool>(nameof(IsSelectionRangeEnabled)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IsSelectionRangeEnabled specifies whether to draw SelectionStart Tick and SelectionEnd Tick or not.
|
||||
|
/// </summary>
|
||||
|
public bool IsSelectionRangeEnabled |
||||
|
{ |
||||
|
get { return GetValue(IsSelectionRangeEnabledProperty); } |
||||
|
set { SetValue(IsSelectionRangeEnabledProperty, value); } |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="TickFrequency"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> TickFrequencyProperty = |
||||
|
AvaloniaProperty.Register<TickBar, double>(nameof(TickFrequency), 0d); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TickFrequency property defines how the tick will be drawn.
|
||||
|
/// </summary>
|
||||
|
public double TickFrequency |
||||
|
{ |
||||
|
get { return GetValue(TickFrequencyProperty); } |
||||
|
set { SetValue(TickFrequencyProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Orientation"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<Orientation> OrientationProperty = |
||||
|
AvaloniaProperty.Register<TickBar, Orientation>(nameof(Orientation)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TickBar parent's orientation.
|
||||
|
/// </summary>
|
||||
|
public Orientation Orientation |
||||
|
{ |
||||
|
get { return GetValue(OrientationProperty); } |
||||
|
set { SetValue(OrientationProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Ticks"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<List<double>> TicksProperty = |
||||
|
AvaloniaProperty.Register<TickBar, List<double>>(nameof(Ticks)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The Ticks property contains collection of value of type Double which
|
||||
|
/// are the logical positions use to draw the ticks.
|
||||
|
/// The property value is a <see cref="DoubleCollection" />.
|
||||
|
/// </summary>
|
||||
|
public List<double> Ticks |
||||
|
{ |
||||
|
get { return GetValue(TicksProperty); } |
||||
|
set { SetValue(TicksProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="IsDirectionReversed"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> IsDirectionReversedProperty = |
||||
|
AvaloniaProperty.Register<TickBar, bool>(nameof(IsDirectionReversed), false); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The IsDirectionReversed property defines the direction of value incrementation.
|
||||
|
/// By default, if Tick's orientation is Horizontal, ticks will be drawn from left to right.
|
||||
|
/// (And, bottom to top for Vertical orientation).
|
||||
|
/// If IsDirectionReversed is 'true' the direction of the drawing will be in opposite direction.
|
||||
|
/// </summary>
|
||||
|
public bool IsDirectionReversed |
||||
|
{ |
||||
|
get { return GetValue(IsDirectionReversedProperty); } |
||||
|
set { SetValue(IsDirectionReversedProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Placement"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<TickBarPlacement> PlacementProperty = |
||||
|
AvaloniaProperty.Register<TickBar, TickBarPlacement>(nameof(Placement), 0d); |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Placement property specified how the Tick will be placed.
|
||||
|
/// This property affects the way ticks are drawn.
|
||||
|
/// This property has type of <see cref="TickBarPlacement" />.
|
||||
|
/// </summary>
|
||||
|
public TickBarPlacement Placement |
||||
|
{ |
||||
|
get { return GetValue(PlacementProperty); } |
||||
|
set { SetValue(PlacementProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private static bool IsValidTickBarPlacement(object o) |
||||
|
{ |
||||
|
TickBarPlacement placement = (TickBarPlacement)o; |
||||
|
return placement == TickBarPlacement.Left || |
||||
|
placement == TickBarPlacement.Top || |
||||
|
placement == TickBarPlacement.Right || |
||||
|
placement == TickBarPlacement.Bottom; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="ReservedSpace"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<Rect> ReservedSpaceProperty = |
||||
|
AvaloniaProperty.Register<TickBar, Rect>(nameof(ReservedSpace)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TickBar will use ReservedSpaceProperty for left and right spacing (for horizontal orientation) or
|
||||
|
/// tob and bottom spacing (for vertical orienation).
|
||||
|
/// The space on both sides of TickBar is half of specified ReservedSpace.
|
||||
|
/// This property has type of <see cref="Rect" />.
|
||||
|
/// </summary>
|
||||
|
public Rect ReservedSpace |
||||
|
{ |
||||
|
get { return GetValue(ReservedSpaceProperty); } |
||||
|
set { SetValue(ReservedSpaceProperty, value); } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Draw ticks.
|
||||
|
/// Ticks can be draw in 8 diffrent ways depends on Placment property and IsDirectionReversed property.
|
||||
|
///
|
||||
|
/// This function also draw selection-tick(s) if IsSelectionRangeEnabled is 'true' and
|
||||
|
/// SelectionStart and SelectionEnd are valid.
|
||||
|
///
|
||||
|
/// The primary ticks (for Mininum and Maximum value) height will be 100% of TickBar's render size (use Width or Height
|
||||
|
/// depends on Placement property).
|
||||
|
///
|
||||
|
/// The secondary ticks (all other ticks, including selection-tics) height will be 75% of TickBar's render size.
|
||||
|
///
|
||||
|
/// Brush that use to fill ticks is specified by Shape.Fill property.
|
||||
|
///
|
||||
|
/// Pen that use to draw ticks is specified by Shape.Pen property.
|
||||
|
/// </summary>
|
||||
|
public override void Render(DrawingContext dc) |
||||
|
{ |
||||
|
var size = new Size(Bounds.Width, Bounds.Height); |
||||
|
var range = Maximum - Minimum; |
||||
|
var tickLen = 0.0d; // Height for Primary Tick (for Mininum and Maximum value)
|
||||
|
var tickLen2 = 0.0d; // Height for Secondary Tick
|
||||
|
var logicalToPhysical = 1.0; |
||||
|
var progression = 1.0d; |
||||
|
var startPoint = new Point(); |
||||
|
var endPoint = new Point(); |
||||
|
var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height; |
||||
|
|
||||
|
// Take Thumb size in to account
|
||||
|
double halfReservedSpace = rSpace * 0.5; |
||||
|
|
||||
|
switch (Placement) |
||||
|
{ |
||||
|
case TickBarPlacement.Top: |
||||
|
if (MathUtilities.GreaterThanOrClose(rSpace, size.Width)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
size = new Size(size.Width - rSpace, size.Height); |
||||
|
tickLen = -size.Height; |
||||
|
startPoint = new Point(halfReservedSpace, size.Height); |
||||
|
endPoint = new Point(halfReservedSpace + size.Width, size.Height); |
||||
|
logicalToPhysical = size.Width / range; |
||||
|
progression = 1; |
||||
|
break; |
||||
|
|
||||
|
case TickBarPlacement.Bottom: |
||||
|
if (MathUtilities.GreaterThanOrClose(rSpace, size.Width)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
size = new Size(size.Width - rSpace, size.Height); |
||||
|
tickLen = size.Height; |
||||
|
startPoint = new Point(halfReservedSpace, 0d); |
||||
|
endPoint = new Point(halfReservedSpace + size.Width, 0d); |
||||
|
logicalToPhysical = size.Width / range; |
||||
|
progression = 1; |
||||
|
break; |
||||
|
|
||||
|
case TickBarPlacement.Left: |
||||
|
if (MathUtilities.GreaterThanOrClose(rSpace, size.Height)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
size = new Size(size.Width, size.Height - rSpace); |
||||
|
|
||||
|
tickLen = -size.Width; |
||||
|
startPoint = new Point(size.Width, size.Height + halfReservedSpace); |
||||
|
endPoint = new Point(size.Width, halfReservedSpace); |
||||
|
logicalToPhysical = size.Height / range * -1; |
||||
|
progression = -1; |
||||
|
break; |
||||
|
|
||||
|
case TickBarPlacement.Right: |
||||
|
if (MathUtilities.GreaterThanOrClose(rSpace, size.Height)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
size = new Size(size.Width, size.Height - rSpace); |
||||
|
|
||||
|
tickLen = size.Width; |
||||
|
startPoint = new Point(0d, size.Height + halfReservedSpace); |
||||
|
endPoint = new Point(0d, halfReservedSpace); |
||||
|
logicalToPhysical = size.Height / range * -1; |
||||
|
progression = -1; |
||||
|
break; |
||||
|
}; |
||||
|
|
||||
|
tickLen2 = tickLen * 0.75; |
||||
|
|
||||
|
// Invert direciton of the ticks
|
||||
|
if (IsDirectionReversed) |
||||
|
{ |
||||
|
progression *= -progression; |
||||
|
logicalToPhysical *= -1; |
||||
|
|
||||
|
// swap startPoint & endPoint
|
||||
|
var pt = startPoint; |
||||
|
startPoint = endPoint; |
||||
|
endPoint = pt; |
||||
|
} |
||||
|
|
||||
|
var pen = new Pen(Fill, 1.0d); |
||||
|
bool snapsToDevicePixels = UseLayoutRounding; |
||||
|
// var xLines = snapsToDevicePixels ? new List<double>() : null;
|
||||
|
// var yLines = snapsToDevicePixels ? new List<double>() : null;
|
||||
|
|
||||
|
// Is it Vertical?
|
||||
|
if ((Placement == TickBarPlacement.Left) || (Placement == TickBarPlacement.Right)) |
||||
|
{ |
||||
|
// Reduce tick interval if it is more than would be visible on the screen
|
||||
|
double interval = TickFrequency; |
||||
|
if (interval > 0.0) |
||||
|
{ |
||||
|
double minInterval = (Maximum - Minimum) / size.Height; |
||||
|
if (interval < minInterval) |
||||
|
{ |
||||
|
interval = minInterval; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Draw Min & Max tick
|
||||
|
dc.DrawLine(pen, startPoint, new Point(startPoint.X + tickLen, startPoint.Y)); |
||||
|
dc.DrawLine(pen, new Point(startPoint.X, endPoint.Y), |
||||
|
new Point(startPoint.X + tickLen, endPoint.Y)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// xLines.Add(startPoint.X);
|
||||
|
// yLines.Add(startPoint.Y - 0.5);
|
||||
|
// xLines.Add(startPoint.X + tickLen);
|
||||
|
// yLines.Add(endPoint.Y - 0.5);
|
||||
|
// xLines.Add(startPoint.X + tickLen2);
|
||||
|
// }
|
||||
|
|
||||
|
// This property is rarely set so let's try to avoid the GetValue
|
||||
|
// caching of the mutable default value
|
||||
|
var ticks = Ticks ?? null; |
||||
|
|
||||
|
// Draw ticks using specified Ticks collection
|
||||
|
if (ticks?.Count > 0) |
||||
|
{ |
||||
|
for (int i = 0; i < ticks.Count; i++) |
||||
|
{ |
||||
|
if (MathUtilities.LessThanOrClose(ticks[i],Minimum) || MathUtilities.GreaterThanOrClose(ticks[i],Maximum)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
double adjustedTick = ticks[i] - Minimum; |
||||
|
|
||||
|
double y = adjustedTick * logicalToPhysical + startPoint.Y; |
||||
|
dc.DrawLine(pen, |
||||
|
new Point(startPoint.X, y), |
||||
|
new Point(startPoint.X + tickLen2, y)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// yLines.Add(y - 0.5);
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
// Draw ticks using specified TickFrequency
|
||||
|
else if (interval > 0.0) |
||||
|
{ |
||||
|
for (double i = interval; i < range; i += interval) |
||||
|
{ |
||||
|
double y = i * logicalToPhysical + startPoint.Y; |
||||
|
|
||||
|
dc.DrawLine(pen, |
||||
|
new Point(startPoint.X, y), |
||||
|
new Point(startPoint.X + tickLen2, y)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// yLines.Add(y - 0.5);
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Draw Selection Ticks
|
||||
|
// if (IsSelectionRangeEnabled)
|
||||
|
// {
|
||||
|
// double y0 = (SelectionStart - Minimum) * logicalToPhysical + startPoint.Y;
|
||||
|
// Point pt0 = new Point(startPoint.X, y0);
|
||||
|
// Point pt1 = new Point(startPoint.X + tickLen2, y0);
|
||||
|
// Point pt2 = new Point(startPoint.X + tickLen2, y0 + Math.Abs(tickLen2) * progression);
|
||||
|
|
||||
|
// PathSegment[] segments = new PathSegment[] {
|
||||
|
// new LineSegment(pt2, true),
|
||||
|
// new LineSegment(pt0, true),
|
||||
|
// };
|
||||
|
// PathGeometry geo = new PathGeometry(new PathFigure[] { new PathFigure(pt1, segments, true) });
|
||||
|
|
||||
|
// dc.DrawGeometry(Fill, pen, geo);
|
||||
|
|
||||
|
// y0 = (SelectionEnd - Minimum) * logicalToPhysical + startPoint.Y;
|
||||
|
// pt0 = new Point(startPoint.X, y0);
|
||||
|
// pt1 = new Point(startPoint.X + tickLen2, y0);
|
||||
|
// pt2 = new Point(startPoint.X + tickLen2, y0 - Math.Abs(tickLen2) * progression);
|
||||
|
|
||||
|
// segments = new PathSegment[] {
|
||||
|
// new LineSegment(pt2, true),
|
||||
|
// new LineSegment(pt0, true),
|
||||
|
// };
|
||||
|
// geo = new PathGeometry(new PathFigure[] { new PathFigure(pt1, segments, true) });
|
||||
|
// dc.DrawGeometry(Fill, pen, geo);
|
||||
|
// }
|
||||
|
} |
||||
|
else // Placement == Top || Placement == Bottom
|
||||
|
{ |
||||
|
// Reduce tick interval if it is more than would be visible on the screen
|
||||
|
double interval = TickFrequency; |
||||
|
if (interval > 0.0) |
||||
|
{ |
||||
|
double minInterval = (Maximum - Minimum) / size.Width; |
||||
|
if (interval < minInterval) |
||||
|
{ |
||||
|
interval = minInterval; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Draw Min & Max tick
|
||||
|
dc.DrawLine(pen, startPoint, new Point(startPoint.X, startPoint.Y + tickLen)); |
||||
|
dc.DrawLine(pen, new Point(endPoint.X, startPoint.Y), |
||||
|
new Point(endPoint.X, startPoint.Y + tickLen)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// xLines.Add(startPoint.X - 0.5);
|
||||
|
// yLines.Add(startPoint.Y);
|
||||
|
// xLines.Add(startPoint.X - 0.5);
|
||||
|
// yLines.Add(endPoint.Y + tickLen);
|
||||
|
// yLines.Add(endPoint.Y + tickLen2);
|
||||
|
// }
|
||||
|
|
||||
|
// This property is rarely set so let's try to avoid the GetValue
|
||||
|
// caching of the mutable default value
|
||||
|
var ticks = Ticks ?? null; |
||||
|
|
||||
|
// Draw ticks using specified Ticks collection
|
||||
|
if (ticks?.Count > 0) |
||||
|
{ |
||||
|
for (int i = 0; i < ticks.Count; i++) |
||||
|
{ |
||||
|
if (MathUtilities.LessThanOrClose(ticks[i],Minimum) || MathUtilities.GreaterThanOrClose(ticks[i],Maximum)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
double adjustedTick = ticks[i] - Minimum; |
||||
|
|
||||
|
double x = adjustedTick * logicalToPhysical + startPoint.X; |
||||
|
dc.DrawLine(pen, |
||||
|
new Point(x, startPoint.Y), |
||||
|
new Point(x, startPoint.Y + tickLen2)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// xLines.Add(x - 0.5);
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
// Draw ticks using specified TickFrequency
|
||||
|
else if (interval > 0.0) |
||||
|
{ |
||||
|
for (double i = interval; i < range; i += interval) |
||||
|
{ |
||||
|
double x = i * logicalToPhysical + startPoint.X; |
||||
|
dc.DrawLine(pen, |
||||
|
new Point(x, startPoint.Y), |
||||
|
new Point(x, startPoint.Y + tickLen2)); |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// xLines.Add(x - 0.5);
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Draw Selection Ticks
|
||||
|
// if (IsSelectionRangeEnabled)
|
||||
|
// {
|
||||
|
// double x0 = (SelectionStart - Minimum) * logicalToPhysical + startPoint.X;
|
||||
|
// Point pt0 = new Point(x0, startPoint.Y);
|
||||
|
// Point pt1 = new Point(x0, startPoint.Y + tickLen2);
|
||||
|
// Point pt2 = new Point(x0 + Math.Abs(tickLen2) * progression, startPoint.Y + tickLen2);
|
||||
|
|
||||
|
// PathSegment[] segments = new PathSegment[] {
|
||||
|
// new LineSegment(pt2, true),
|
||||
|
// new LineSegment(pt0, true),
|
||||
|
// };
|
||||
|
// PathGeometry geo = new PathGeometry(new PathFigure[] { new PathFigure(pt1, segments, true) });
|
||||
|
|
||||
|
// dc.DrawGeometry(Fill, pen, geo);
|
||||
|
|
||||
|
// x0 = (SelectionEnd - Minimum) * logicalToPhysical + startPoint.X;
|
||||
|
// pt0 = new Point(x0, startPoint.Y);
|
||||
|
// pt1 = new Point(x0, startPoint.Y + tickLen2);
|
||||
|
// pt2 = new Point(x0 - Math.Abs(tickLen2) * progression, startPoint.Y + tickLen2);
|
||||
|
|
||||
|
// segments = new PathSegment[] {
|
||||
|
// new LineSegment(pt2, true),
|
||||
|
// new LineSegment(pt0, true),
|
||||
|
// };
|
||||
|
// geo = new PathGeometry(new PathFigure[] { new PathFigure(pt1, segments, true) });
|
||||
|
// dc.DrawGeometry(Fill, pen, geo);
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
// if (snapsToDevicePixels)
|
||||
|
// {
|
||||
|
// xLines.Add(Bounds.Width);
|
||||
|
// yLines.Add(Bounds.Height);
|
||||
|
// VisualXSnappingGuidelines = xLines;
|
||||
|
// VisualYSnappingGuidelines = yLines;
|
||||
|
// }
|
||||
|
} |
||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) |
||||
|
{ |
||||
|
if (TemplatedParent is Slider parent) |
||||
|
{ |
||||
|
this[~TickFrequencyProperty] = parent[~Slider.TickFrequencyProperty]; |
||||
|
this[~OrientationProperty] = parent[~Slider.OrientationProperty]; |
||||
|
this[~MinimumProperty] = parent[~Slider.MinimumProperty]; |
||||
|
this[~MaximumProperty] = parent[~Slider.MaximumProperty]; |
||||
|
|
||||
|
// if (parent._track != null)
|
||||
|
// {
|
||||
|
ReservedSpace = new Rect(0, 0, 20, 20); // HACK parent._track.Thumb[~BoundsProperty];
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue