1436 lines
46 KiB
C#
1436 lines
46 KiB
C#
|
|
using System.Diagnostics;
|
|||
|
|
using System.Runtime.CompilerServices;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
using System.Windows.Threading;
|
|||
|
|
using WPFDark.Internals;
|
|||
|
|
|
|||
|
|
|
|||
|
|
namespace WPFDark.Controls
|
|||
|
|
{
|
|||
|
|
public enum BiaNumberEditorMode
|
|||
|
|
{
|
|||
|
|
Simple,
|
|||
|
|
WideRange
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class BiaNumberEditor : FrameworkElement
|
|||
|
|
{
|
|||
|
|
#region IsReadOnly
|
|||
|
|
|
|||
|
|
public bool IsReadOnly
|
|||
|
|
{
|
|||
|
|
get => _IsReadOnly;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _IsReadOnly)
|
|||
|
|
SetValue(IsReadOnlyProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool _IsReadOnly;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty IsReadOnlyProperty =
|
|||
|
|
DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
false,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._IsReadOnly = (bool) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region BorderColor
|
|||
|
|
|
|||
|
|
public ByteColor BorderColor
|
|||
|
|
{
|
|||
|
|
get => _BorderColor;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _BorderColor)
|
|||
|
|
SetValue(BorderColorProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private ByteColor _BorderColor = ByteColor.Red;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty BorderColorProperty =
|
|||
|
|
DependencyProperty.Register(nameof(BorderColor), typeof(ByteColor), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
Boxes.ByteColorRed,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._BorderColor = (ByteColor) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region SliderBrush
|
|||
|
|
|
|||
|
|
public Brush? SliderBrush
|
|||
|
|
{
|
|||
|
|
get => _SliderBrush;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _SliderBrush)
|
|||
|
|
SetValue(SliderBrushProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Brush? _SliderBrush = Brushes.GreenYellow;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty SliderBrushProperty =
|
|||
|
|
DependencyProperty.Register(nameof(SliderBrush), typeof(Brush), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
Brushes.GreenYellow,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._SliderBrush = (Brush) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Value
|
|||
|
|
|
|||
|
|
public double Value
|
|||
|
|
{
|
|||
|
|
get => _Value;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _Value) == false)
|
|||
|
|
SetValue(ValueProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _Value;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty ValueProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Value), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
0.0,
|
|||
|
|
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
// 最小値・最大値でクランプして保存する
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Value = self.ClampValue((double) e.NewValue);
|
|||
|
|
|
|||
|
|
self.ValueChanged?.Invoke(self, EventArgs.Empty);
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Caption
|
|||
|
|
|
|||
|
|
public string? Caption
|
|||
|
|
{
|
|||
|
|
get => _Caption;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _Caption)
|
|||
|
|
SetValue(CaptionProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string? _Caption;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty CaptionProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Caption), typeof(string), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
default(string),
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Caption = e.NewValue?.ToString() ?? "";
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region SliderMinimum
|
|||
|
|
|
|||
|
|
public double SliderMinimum
|
|||
|
|
{
|
|||
|
|
get => _SliderMinimum;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _SliderMinimum) == false)
|
|||
|
|
SetValue(SliderMinimumProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _SliderMinimum = double.MinValue;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty SliderMinimumProperty =
|
|||
|
|
DependencyProperty.Register(nameof(SliderMinimum), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
double.MinValue,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._SliderMinimum = (double) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region SliderMaximum
|
|||
|
|
|
|||
|
|
public double SliderMaximum
|
|||
|
|
{
|
|||
|
|
get => _SliderMaximum;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _SliderMaximum) == false)
|
|||
|
|
SetValue(SliderMaximumProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _SliderMaximum = double.MaxValue;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty SliderMaximumProperty =
|
|||
|
|
DependencyProperty.Register(nameof(SliderMaximum), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
double.MaxValue,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._SliderMaximum = (double) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Minimum
|
|||
|
|
|
|||
|
|
public double Minimum
|
|||
|
|
{
|
|||
|
|
get => _Minimum;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _Minimum) == false)
|
|||
|
|
SetValue(MinimumProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _Minimum = double.MinValue;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty MinimumProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Minimum), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
double.MinValue,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
// 変更後の最小値でValueをクランプする
|
|||
|
|
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Minimum = (double) e.NewValue;
|
|||
|
|
|
|||
|
|
self.Value = self.ClampValue(self.Value);
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Maximum
|
|||
|
|
|
|||
|
|
public double Maximum
|
|||
|
|
{
|
|||
|
|
get => _Maximum;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _Maximum) == false)
|
|||
|
|
SetValue(MaximumProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _Maximum = double.MaxValue;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty MaximumProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Maximum), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
double.MaxValue,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
// 変更後の最大値でValueをクランプする
|
|||
|
|
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Maximum = (double) e.NewValue;
|
|||
|
|
|
|||
|
|
self.Value = self.ClampValue(self.Value);
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region DisplayFormat
|
|||
|
|
|
|||
|
|
public string DisplayFormat
|
|||
|
|
{
|
|||
|
|
get => _DisplayFormat;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _DisplayFormat)
|
|||
|
|
SetValue(DisplayFormatProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string _DisplayFormat = "F3";
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty DisplayFormatProperty =
|
|||
|
|
DependencyProperty.Register(nameof(DisplayFormat), typeof(string), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
"F3",
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._DisplayFormat = e.NewValue?.ToString() ?? "";
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region UnitString
|
|||
|
|
|
|||
|
|
public string UnitString
|
|||
|
|
{
|
|||
|
|
get => _UnitString;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _UnitString)
|
|||
|
|
SetValue(UnitStringProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string _UnitString = "";
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty UnitStringProperty =
|
|||
|
|
DependencyProperty.Register(nameof(UnitString), typeof(string), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
"",
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._UnitString = e.NewValue?.ToString() ?? "";
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Background
|
|||
|
|
|
|||
|
|
public Brush? Background
|
|||
|
|
{
|
|||
|
|
get => _Background;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _Background)
|
|||
|
|
SetValue(BackgroundProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Brush? _Background;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty BackgroundProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
default(Brush),
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Background = (Brush) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Foreground
|
|||
|
|
|
|||
|
|
public Brush? Foreground
|
|||
|
|
{
|
|||
|
|
get => _Foreground;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _Foreground)
|
|||
|
|
SetValue(ForegroundProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Brush? _Foreground;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty ForegroundProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Foreground), typeof(Brush), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
default(Brush),
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Foreground = (Brush) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region CaptionForeground
|
|||
|
|
|
|||
|
|
public Brush? CaptionForeground
|
|||
|
|
{
|
|||
|
|
get => _CaptionForeground;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _CaptionForeground)
|
|||
|
|
SetValue(CaptionForegroundProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Brush? _CaptionForeground;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty CaptionForegroundProperty =
|
|||
|
|
DependencyProperty.Register(nameof(CaptionForeground), typeof(Brush), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
default(Brush),
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._CaptionForeground = (Brush) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Padding
|
|||
|
|
|
|||
|
|
public Thickness Padding
|
|||
|
|
{
|
|||
|
|
get => _Padding;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _Padding)
|
|||
|
|
SetValue(PaddingProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Thickness _Padding;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty PaddingProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Padding), typeof(Thickness), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
new Thickness(0),
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Padding = (Thickness) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Mode
|
|||
|
|
|
|||
|
|
public BiaNumberEditorMode Mode
|
|||
|
|
{
|
|||
|
|
get => _Mode;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _Mode)
|
|||
|
|
SetValue(ModeProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private BiaNumberEditorMode _Mode = BiaNumberEditorMode.Simple;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty ModeProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Mode), typeof(BiaNumberEditorMode), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
BiaNumberEditorMode.Simple,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Mode = (BiaNumberEditorMode) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Increment
|
|||
|
|
|
|||
|
|
public double Increment
|
|||
|
|
{
|
|||
|
|
get => _Increment;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _Increment) == false)
|
|||
|
|
SetValue(IncrementProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _Increment = 1.0;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty IncrementProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Increment), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
1.0,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._Increment = (double) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region CornerRadius
|
|||
|
|
|
|||
|
|
public double CornerRadius
|
|||
|
|
{
|
|||
|
|
get => _CornerRadius;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreClose(value, _CornerRadius) == false)
|
|||
|
|
SetValue(CornerRadiusProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double _CornerRadius = Constants.BasicCornerRadiusPrim;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty CornerRadiusProperty =
|
|||
|
|
DependencyProperty.Register(nameof(CornerRadius), typeof(double), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
Boxes.BasicCornerRadiusPrim,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._CornerRadius = (double) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region IsVisibleBorder
|
|||
|
|
|
|||
|
|
public bool IsVisibleBorder
|
|||
|
|
{
|
|||
|
|
get => _IsVisibleBorder;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _IsVisibleBorder)
|
|||
|
|
SetValue(IsVisibleBorderProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool _IsVisibleBorder = true;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty IsVisibleBorderProperty =
|
|||
|
|
DependencyProperty.Register(nameof(IsVisibleBorder), typeof(bool), typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
true,
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._IsVisibleBorder = (bool) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region StartedContinuousEditingCommand
|
|||
|
|
|
|||
|
|
public ICommand? StartedContinuousEditingCommand
|
|||
|
|
{
|
|||
|
|
get => _StartedContinuousEditingCommand;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _StartedContinuousEditingCommand)
|
|||
|
|
SetValue(StartedContinuousEditingCommandProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private ICommand? _StartedContinuousEditingCommand;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty StartedContinuousEditingCommandProperty =
|
|||
|
|
DependencyProperty.Register(
|
|||
|
|
nameof(StartedContinuousEditingCommand),
|
|||
|
|
typeof(ICommand),
|
|||
|
|
typeof(BiaNumberEditor),
|
|||
|
|
new PropertyMetadata(
|
|||
|
|
default(ICommand),
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._StartedContinuousEditingCommand = (ICommand) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region EndContinuousEditingCommand
|
|||
|
|
|
|||
|
|
public ICommand? EndContinuousEditingCommand
|
|||
|
|
{
|
|||
|
|
get => _EndContinuousEditingCommand;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _EndContinuousEditingCommand)
|
|||
|
|
SetValue(EndContinuousEditingCommandProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private ICommand? _EndContinuousEditingCommand;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty EndContinuousEditingCommandProperty =
|
|||
|
|
DependencyProperty.Register(
|
|||
|
|
nameof(EndContinuousEditingCommand),
|
|||
|
|
typeof(ICommand),
|
|||
|
|
typeof(BiaNumberEditor),
|
|||
|
|
new PropertyMetadata(
|
|||
|
|
default(ICommand),
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._EndContinuousEditingCommand = (ICommand) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region TextTrimming
|
|||
|
|
|
|||
|
|
public BiaTextTrimmingMode TextTrimming
|
|||
|
|
{
|
|||
|
|
get => _TextTrimming;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _TextTrimming)
|
|||
|
|
SetValue(TextTrimmingProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private BiaTextTrimmingMode _TextTrimming = BiaTextTrimmingMode.Standard;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty TextTrimmingProperty =
|
|||
|
|
DependencyProperty.Register(
|
|||
|
|
nameof(TextTrimming),
|
|||
|
|
typeof(BiaTextTrimmingMode),
|
|||
|
|
typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
BiaTextTrimmingMode.Standard,
|
|||
|
|
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._TextTrimming = (BiaTextTrimmingMode) e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region CaptionAlignment
|
|||
|
|
|
|||
|
|
public TextAlignment CaptionAlignment
|
|||
|
|
{
|
|||
|
|
get => _CaptionAlignment;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _CaptionAlignment)
|
|||
|
|
SetValue(CaptionAlignmentProperty, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TextAlignment _CaptionAlignment = TextAlignment.Left;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty CaptionAlignmentProperty =
|
|||
|
|
DependencyProperty.Register(
|
|||
|
|
nameof(CaptionAlignment),
|
|||
|
|
typeof(TextAlignment),
|
|||
|
|
typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
TextAlignment.Left,
|
|||
|
|
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._CaptionAlignment = (TextAlignment)e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region ValueAlignment
|
|||
|
|
|
|||
|
|
public TextAlignment ValueAlignment
|
|||
|
|
{
|
|||
|
|
get => _ValueAlignment;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
if (value != _ValueAlignment)
|
|||
|
|
SetValue(CaptionAlignmentProperty,value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TextAlignment _ValueAlignment = TextAlignment.Right;
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty ValueAlignmentProperty =
|
|||
|
|
DependencyProperty.Register(
|
|||
|
|
nameof(ValueAlignment),
|
|||
|
|
typeof(TextAlignment),
|
|||
|
|
typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(
|
|||
|
|
TextAlignment.Right,
|
|||
|
|
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
|||
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|||
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|||
|
|
(s, e) =>
|
|||
|
|
{
|
|||
|
|
var self = (BiaNumberEditor) s;
|
|||
|
|
self._ValueAlignment = (TextAlignment)e.NewValue;
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
// ReSharper disable once EventNeverSubscribedTo.Global
|
|||
|
|
public event EventHandler? ValueChanged;
|
|||
|
|
|
|||
|
|
static BiaNumberEditor()
|
|||
|
|
{
|
|||
|
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(BiaNumberEditor),
|
|||
|
|
new FrameworkPropertyMetadata(typeof(BiaNumberEditor)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public BiaNumberEditor()
|
|||
|
|
{
|
|||
|
|
IsEnabledChanged += (_, _) => InvalidateVisual();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnRender(DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
if (ActualWidth <= 1 ||
|
|||
|
|
ActualHeight <= 1)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var rounder = new LayoutRounder(this);
|
|||
|
|
|
|||
|
|
DrawBackground(rounder, dc);
|
|||
|
|
|
|||
|
|
var isCornerRadiusZero = NumberHelper.AreCloseZero(CornerRadius);
|
|||
|
|
|
|||
|
|
if (isCornerRadiusZero == false)
|
|||
|
|
dc.PushClip(
|
|||
|
|
Caches.GetClipGeom(rounder, ActualWidth, ActualHeight, CornerRadius, IsVisibleBorder));
|
|||
|
|
{
|
|||
|
|
if (Mode == BiaNumberEditorMode.Simple)
|
|||
|
|
DrawSlider(rounder, dc);
|
|||
|
|
|
|||
|
|
DrawText(dc);
|
|||
|
|
|
|||
|
|
if (IsMouseOver &&
|
|||
|
|
IsEnabled &&
|
|||
|
|
IsReadOnly == false &&
|
|||
|
|
NumberHelper.AreCloseZero(Increment) == false)
|
|||
|
|
DrawSpin(dc);
|
|||
|
|
}
|
|||
|
|
if (isCornerRadiusZero == false)
|
|||
|
|
dc.Pop();
|
|||
|
|
|
|||
|
|
if (IsVisibleBorder)
|
|||
|
|
DrawBorder(rounder, dc);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawBackground(in LayoutRounder rounder, DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
var brush = isEditing
|
|||
|
|
? textBox?.Background
|
|||
|
|
: Background;
|
|||
|
|
|
|||
|
|
if (brush is null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (NumberHelper.AreCloseZero(CornerRadius))
|
|||
|
|
dc.DrawRectangle(
|
|||
|
|
brush,
|
|||
|
|
null,
|
|||
|
|
rounder.RoundRenderRectangle(IsVisibleBorder));
|
|||
|
|
else
|
|||
|
|
dc.DrawRoundedRectangle(
|
|||
|
|
brush,
|
|||
|
|
null,
|
|||
|
|
rounder.RoundRenderRectangle(IsVisibleBorder),
|
|||
|
|
CornerRadius,
|
|||
|
|
CornerRadius);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawBorder(in LayoutRounder rounder, DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
if (NumberHelper.AreCloseZero(CornerRadius))
|
|||
|
|
dc.DrawRectangle(
|
|||
|
|
Brushes.Transparent,
|
|||
|
|
rounder.GetBorderPen(BorderColor),
|
|||
|
|
rounder.RoundRenderRectangle(IsVisibleBorder)
|
|||
|
|
);
|
|||
|
|
else
|
|||
|
|
dc.DrawRoundedRectangle(
|
|||
|
|
Brushes.Transparent,
|
|||
|
|
rounder.GetBorderPen(BorderColor),
|
|||
|
|
rounder.RoundRenderRectangle(IsVisibleBorder),
|
|||
|
|
CornerRadius,
|
|||
|
|
CornerRadius);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DrawSlider(in LayoutRounder rounder, DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
if (SliderWidth <= 0.0f)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var brush = isEditing
|
|||
|
|
? textBox?.Background
|
|||
|
|
: SliderBrush;
|
|||
|
|
|
|||
|
|
if (brush is null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var w = (UiValue - ActualSliderMinimum) * rounder.RoundRenderWidth(IsVisibleBorder) / SliderWidth;
|
|||
|
|
var r = rounder.RoundRenderRectangle(IsVisibleBorder);
|
|||
|
|
r.Width = (rounder.RoundLayoutValue(w), 0.0).Max();
|
|||
|
|
|
|||
|
|
dc.DrawRectangle(brush, null, r);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const double SpinWidth = 14.0;
|
|||
|
|
|
|||
|
|
private void DrawText(DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
var offsetY = (ActualHeight - Constants.BasicOneLineHeight) * 0.5;
|
|||
|
|
|
|||
|
|
var textWidth = ActualWidth - Padding.Left - Padding.Right - SpinWidth * 2;
|
|||
|
|
var textX = Padding.Left + SpinWidth;
|
|||
|
|
var textY = Padding.Top + offsetY;
|
|||
|
|
|
|||
|
|
if (Caption != null &&
|
|||
|
|
CaptionForeground != null)
|
|||
|
|
DefaultTextRenderer.Instance.Draw(
|
|||
|
|
this,
|
|||
|
|
Caption,
|
|||
|
|
textX,
|
|||
|
|
textY,
|
|||
|
|
CaptionForeground,
|
|||
|
|
dc,
|
|||
|
|
textWidth,
|
|||
|
|
CaptionAlignment,
|
|||
|
|
TextTrimming,
|
|||
|
|
false);
|
|||
|
|
|
|||
|
|
if (UiValueString != null &&
|
|||
|
|
Foreground != null)
|
|||
|
|
{
|
|||
|
|
DefaultTextRenderer.Instance.Draw(
|
|||
|
|
this,
|
|||
|
|
UiValueString,
|
|||
|
|
textX,
|
|||
|
|
textY,
|
|||
|
|
Foreground,
|
|||
|
|
dc,
|
|||
|
|
textWidth,
|
|||
|
|
ValueAlignment,
|
|||
|
|
BiaTextTrimmingMode.None,
|
|||
|
|
false);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static readonly Brush? _moBrush = ThemeManager.Current.TryFindResource("AccentBrushKey") as Brush;
|
|||
|
|
|
|||
|
|
private void DrawSpin(DrawingContext dc)
|
|||
|
|
{
|
|||
|
|
if (_decSpinGeom is null)
|
|||
|
|
SetupSpinGeom();
|
|||
|
|
|
|||
|
|
var spinBackground = Caches.GetSolidColorBrush(new ByteColor(0x40, 0x00, 0x00, 0x00));
|
|||
|
|
|
|||
|
|
var offsetY = 8 + (ActualHeight - Constants.BasicOneLineHeight) * 0.5;
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
var offsetX = 5.0;
|
|||
|
|
|
|||
|
|
if (_mouseOverType == MouseOverType.DecSpin)
|
|||
|
|
dc.DrawRectangle(spinBackground, null, new Rect(0, 0, SpinWidth, ActualHeight));
|
|||
|
|
|
|||
|
|
var key = HashCodeMaker.Make(offsetX, offsetY);
|
|||
|
|
|
|||
|
|
if (TranslateTransformCache.TryGetValue(key, out var tt) == false)
|
|||
|
|
{
|
|||
|
|
tt = new TranslateTransform(offsetX, offsetY);
|
|||
|
|
TranslateTransformCache.Add(key, tt);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dc.PushTransform(tt);
|
|||
|
|
dc.DrawGeometry(
|
|||
|
|
_mouseOverType == MouseOverType.DecSpin
|
|||
|
|
? _moBrush
|
|||
|
|
: Foreground, null, _decSpinGeom);
|
|||
|
|
dc.Pop();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
var offsetX = ActualWidth - 5.0 * 2 + 1;
|
|||
|
|
|
|||
|
|
if (_mouseOverType == MouseOverType.IncSpin)
|
|||
|
|
dc.DrawRectangle(spinBackground, null,
|
|||
|
|
new Rect(ActualWidth - SpinWidth, 0, SpinWidth, ActualHeight));
|
|||
|
|
|
|||
|
|
var key = HashCodeMaker.Make(offsetX, offsetY);
|
|||
|
|
|
|||
|
|
if (TranslateTransformCache.TryGetValue(key, out var tt) == false)
|
|||
|
|
{
|
|||
|
|
tt = new TranslateTransform(offsetX, offsetY);
|
|||
|
|
TranslateTransformCache.Add(key, tt);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dc.PushTransform(tt);
|
|||
|
|
dc.DrawGeometry(
|
|||
|
|
_mouseOverType == MouseOverType.IncSpin
|
|||
|
|
? _moBrush
|
|||
|
|
: Foreground, null, _incSpinGeom);
|
|||
|
|
dc.Pop();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private enum MouseOverType
|
|||
|
|
{
|
|||
|
|
None,
|
|||
|
|
DecSpin,
|
|||
|
|
IncSpin,
|
|||
|
|
Slider
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool _isMouseDown;
|
|||
|
|
private bool _isMouseMoved;
|
|||
|
|
private Point _oldPos;
|
|||
|
|
private Point _mouseDownPos;
|
|||
|
|
private MouseOverType _mouseOverTypeOnMouseDown;
|
|||
|
|
private MouseOverType _mouseOverType;
|
|||
|
|
|
|||
|
|
private bool _isContinuousEdited;
|
|||
|
|
private double _ContinuousEditingStartValue;
|
|||
|
|
|
|||
|
|
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnMouseLeftButtonDown(e);
|
|||
|
|
|
|||
|
|
_oldPos = e.GetPosition(this);
|
|||
|
|
_mouseDownPos = _oldPos;
|
|||
|
|
|
|||
|
|
if (IsReadOnly)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
_isMouseDown = true;
|
|||
|
|
_isMouseMoved = false;
|
|||
|
|
_mouseOverTypeOnMouseDown = MakeMouseOverType(e);
|
|||
|
|
_isContinuousEdited = false;
|
|||
|
|
_ContinuousEditingStartValue = Value;
|
|||
|
|
|
|||
|
|
if (_mouseOverTypeOnMouseDown == MouseOverType.Slider)
|
|||
|
|
CaptureMouse();
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const double ClickPlayWidth = 4;
|
|||
|
|
|
|||
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnMouseMove(e);
|
|||
|
|
|
|||
|
|
var newMouseOverType = MakeMouseOverType(e);
|
|||
|
|
|
|||
|
|
if (_mouseOverType != newMouseOverType)
|
|||
|
|
{
|
|||
|
|
_mouseOverType = newMouseOverType;
|
|||
|
|
InvalidateVisual();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (_isMouseDown == false)
|
|||
|
|
{
|
|||
|
|
Cursor = _mouseOverType == MouseOverType.Slider && IsReadOnly == false ? Cursors.SizeWE : Cursors.Arrow;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (IsReadOnly)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// Down直後マウス移動ない場合でもOnMouseMoveが呼ばれることがあるため、判定する。
|
|||
|
|
var currentPos = e.GetPosition(this);
|
|||
|
|
if (currentPos == _oldPos)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (_isMouseMoved == false)
|
|||
|
|
if (Math.Abs(currentPos.X - _mouseDownPos.X) <= ClickPlayWidth)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (_mouseOverTypeOnMouseDown != MouseOverType.Slider)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
var oldValue = Value;
|
|||
|
|
var newValue = Value;
|
|||
|
|
|
|||
|
|
switch (Mode)
|
|||
|
|
{
|
|||
|
|
case BiaNumberEditorMode.Simple:
|
|||
|
|
{
|
|||
|
|
// 0から1
|
|||
|
|
var xr = (currentPos.X, 0.0, ActualWidth).Clamp() / ActualWidth;
|
|||
|
|
newValue = SliderWidth * xr + ActualSliderMinimum;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case BiaNumberEditorMode.WideRange:
|
|||
|
|
{
|
|||
|
|
// Ctrl押下中は5倍速い
|
|||
|
|
var s = KeyboardHelper.IsPressControl
|
|||
|
|
? 5.0
|
|||
|
|
: 1.0;
|
|||
|
|
var w = currentPos.X - _oldPos.X;
|
|||
|
|
var v = oldValue + s * w * Increment;
|
|||
|
|
|
|||
|
|
newValue = (v, ActualSliderMinimum, ActualSliderMaximum).Clamp();
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (NumberHelper.AreClose(newValue, oldValue) == false)
|
|||
|
|
{
|
|||
|
|
if (_isContinuousEdited == false)
|
|||
|
|
{
|
|||
|
|
_isContinuousEdited = true;
|
|||
|
|
|
|||
|
|
StartedContinuousEditingCommand?.ExecuteIfCan(null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Value = newValue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_mouseOverType = MouseOverType.Slider;
|
|||
|
|
_isMouseMoved = true;
|
|||
|
|
_oldPos = currentPos;
|
|||
|
|
|
|||
|
|
Cursor = _mouseOverType == MouseOverType.Slider && IsReadOnly == false ? Cursors.SizeWE : Cursors.Arrow;
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnMouseLeftButtonUp(e);
|
|||
|
|
|
|||
|
|
if (IsReadOnly == false)
|
|||
|
|
{
|
|||
|
|
if (_mouseOverTypeOnMouseDown == MouseOverType.Slider)
|
|||
|
|
ReleaseMouseCapture();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_isMouseDown = false;
|
|||
|
|
|
|||
|
|
if (_isMouseMoved)
|
|||
|
|
{
|
|||
|
|
if (_isContinuousEdited)
|
|||
|
|
{
|
|||
|
|
if (EndContinuousEditingCommand != null)
|
|||
|
|
{
|
|||
|
|
if (EndContinuousEditingCommand.CanExecute(null))
|
|||
|
|
{
|
|||
|
|
var changedValue = Value;
|
|||
|
|
Value = _ContinuousEditingStartValue;
|
|||
|
|
|
|||
|
|
EndContinuousEditingCommand.Execute(null);
|
|||
|
|
|
|||
|
|
Value = changedValue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_isContinuousEdited = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var p = e.GetPosition(this);
|
|||
|
|
|
|||
|
|
if (Math.Abs(p.X - _mouseDownPos.X) <= ClickPlayWidth)
|
|||
|
|
{
|
|||
|
|
// Ctrl押下中は5倍速い
|
|||
|
|
var inc = KeyboardHelper.IsPressControl
|
|||
|
|
? Increment * 5
|
|||
|
|
: Increment;
|
|||
|
|
|
|||
|
|
if (p.X <= SpinWidth && IsReadOnly == false)
|
|||
|
|
AddValue(-inc);
|
|||
|
|
else if (p.X >= ActualWidth - SpinWidth && IsReadOnly == false)
|
|||
|
|
AddValue(inc);
|
|||
|
|
else
|
|||
|
|
ShowEditBox();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_mouseOverType = MakeMouseOverType(e);
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnMouseLeave(MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnMouseLeave(e);
|
|||
|
|
|
|||
|
|
if (_isMouseDown)
|
|||
|
|
{
|
|||
|
|
_isMouseDown = false;
|
|||
|
|
ReleaseMouseCapture();
|
|||
|
|
this.ResetMouseClipping();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_mouseOverType = MouseOverType.None;
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private MouseOverType MakeMouseOverType(MouseEventArgs e)
|
|||
|
|
{
|
|||
|
|
var x = e.GetPosition(this).X;
|
|||
|
|
|
|||
|
|
if (x < 0 || x >= ActualWidth)
|
|||
|
|
return MouseOverType.None;
|
|||
|
|
|
|||
|
|
if (x <= SpinWidth)
|
|||
|
|
return MouseOverType.DecSpin;
|
|||
|
|
|
|||
|
|
if (x >= ActualWidth - SpinWidth)
|
|||
|
|
return MouseOverType.IncSpin;
|
|||
|
|
|
|||
|
|
return MouseOverType.Slider;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private TextBox? textBox;
|
|||
|
|
private bool isEditing;
|
|||
|
|
|
|||
|
|
private void AddValue(double i)
|
|||
|
|
{
|
|||
|
|
var v = Value + i;
|
|||
|
|
|
|||
|
|
Value = ClampValue(v);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void OnKeyDown(KeyEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnKeyDown(e);
|
|||
|
|
|
|||
|
|
if (IsEnabled == false)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (e.Key == Key.Return)
|
|||
|
|
{
|
|||
|
|
ShowEditBox();
|
|||
|
|
e.Handled = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ShowEditBox()
|
|||
|
|
{
|
|||
|
|
if (isEditing)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (textBox is null)
|
|||
|
|
{
|
|||
|
|
textBox = new TextBox
|
|||
|
|
{
|
|||
|
|
IsTabStop = false,
|
|||
|
|
IsUndoEnabled = false,
|
|||
|
|
FocusVisualStyle = null,
|
|||
|
|
SelectionLength = 0
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
textBox.TextChanged += TextBox_OnTextChanged;
|
|||
|
|
textBox.LostFocus += TextBox_OnLostFocus;
|
|||
|
|
textBox.PreviewKeyDown += TextBox_OnPreviewKeyDown;
|
|||
|
|
textBox.PreviewMouseDown += TextBox_OnPreviewMouseDown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
textBox.Width = ActualWidth;
|
|||
|
|
textBox.Height = ActualHeight;
|
|||
|
|
textBox.IsReadOnly = IsReadOnly;
|
|||
|
|
textBox.Text = FormattedValueString;
|
|||
|
|
|
|||
|
|
AddVisualChild(textBox);
|
|||
|
|
InvalidateMeasure();
|
|||
|
|
|
|||
|
|
textBox.SelectAll();
|
|||
|
|
textBox.Focus();
|
|||
|
|
textBox.CaptureMouse();
|
|||
|
|
|
|||
|
|
isEditing = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TextBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
|
|||
|
|
{
|
|||
|
|
var rounder = new LayoutRounder(this);
|
|||
|
|
|
|||
|
|
// 自コントロール上であれば、終了させない
|
|||
|
|
var pos = e.GetPosition(this);
|
|||
|
|
var rect = rounder.RoundRenderRectangle(false);
|
|||
|
|
if (rect.Contains(pos))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (isEditing)
|
|||
|
|
FinishEditing(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
|||
|
|
{
|
|||
|
|
InvalidateVisual();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TextBox_OnLostFocus(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (isEditing)
|
|||
|
|
FinishEditing(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
|
|||
|
|
{
|
|||
|
|
switch (e.Key)
|
|||
|
|
{
|
|||
|
|
case Key.Tab:
|
|||
|
|
{
|
|||
|
|
Debug.Assert(textBox != null);
|
|||
|
|
|
|||
|
|
var v = MakeValueFromString(textBox.Text);
|
|||
|
|
|
|||
|
|
if (v.Result == MakeValueResult.Continue)
|
|||
|
|
textBox.Text = v.Value.ToString(DisplayFormat);
|
|||
|
|
else
|
|||
|
|
FinishEditing(v.Result == MakeValueResult.Ok);
|
|||
|
|
|
|||
|
|
var t = Keyboard.Modifiers == ModifierKeys.Shift
|
|||
|
|
? Caches.PreviousTraversalRequest
|
|||
|
|
: Caches.NextTraversalRequest;
|
|||
|
|
MoveFocus(t);
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case Key.Return:
|
|||
|
|
{
|
|||
|
|
Debug.Assert(textBox != null);
|
|||
|
|
|
|||
|
|
var v = MakeValueFromString(textBox.Text);
|
|||
|
|
|
|||
|
|
if (v.Result == MakeValueResult.Continue)
|
|||
|
|
textBox.Text = v.Value.ToString(DisplayFormat);
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
FinishEditing(v.Result == MakeValueResult.Ok);
|
|||
|
|
Dispatcher?.BeginInvoke(DispatcherPriority.Input, (Action) (() => Focus()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case Key.Escape:
|
|||
|
|
{
|
|||
|
|
Debug.Assert(textBox != null);
|
|||
|
|
|
|||
|
|
textBox.Text = FormattedValueString;
|
|||
|
|
FinishEditing(false);
|
|||
|
|
|
|||
|
|
Dispatcher?.BeginInvoke(DispatcherPriority.Input, (Action) (() => Focus()));
|
|||
|
|
|
|||
|
|
e.Handled = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void FinishEditing(bool isEdit)
|
|||
|
|
{
|
|||
|
|
Debug.Assert(textBox != null);
|
|||
|
|
|
|||
|
|
if (isEdit)
|
|||
|
|
{
|
|||
|
|
var v = MakeValueFromString(textBox.Text);
|
|||
|
|
if (v.Result == MakeValueResult.Ok ||
|
|||
|
|
v.Result == MakeValueResult.Continue)
|
|||
|
|
Value = v.Value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
textBox?.ReleaseMouseCapture();
|
|||
|
|
|
|||
|
|
RemoveVisualChild(textBox);
|
|||
|
|
isEditing = false;
|
|||
|
|
InvalidateVisual();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override int VisualChildrenCount
|
|||
|
|
=> isEditing
|
|||
|
|
? 1
|
|||
|
|
: 0;
|
|||
|
|
|
|||
|
|
protected override Visual GetVisualChild(int index)
|
|||
|
|
=> textBox ?? throw new NullReferenceException();
|
|||
|
|
|
|||
|
|
protected override Size ArrangeOverride(Size finalSize)
|
|||
|
|
{
|
|||
|
|
if (isEditing)
|
|||
|
|
textBox?.Arrange(new Rect(new Point(0, 0), textBox.DesiredSize));
|
|||
|
|
|
|||
|
|
return base.ArrangeOverride(finalSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override Size MeasureOverride(Size availableSize)
|
|||
|
|
{
|
|||
|
|
if (isEditing)
|
|||
|
|
textBox?.Measure(new Size(ActualWidth, ActualHeight));
|
|||
|
|
|
|||
|
|
// todo:DPI変更時に再描画が行われないため明示的に指示している。要調査。
|
|||
|
|
InvalidateVisual();
|
|||
|
|
|
|||
|
|
return base.MeasureOverride(availableSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private enum MakeValueResult
|
|||
|
|
{
|
|||
|
|
Ok,
|
|||
|
|
Cancel,
|
|||
|
|
Continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private (MakeValueResult Result, double Value) MakeValueFromString(string src)
|
|||
|
|
{
|
|||
|
|
if (double.TryParse(src, out var v))
|
|||
|
|
return (MakeValueResult.Ok, ClampValue(v));
|
|||
|
|
|
|||
|
|
if (double.TryParse(Evaluator.Eval(src), out v))
|
|||
|
|
return (MakeValueResult.Continue, ClampValue(v));
|
|||
|
|
|
|||
|
|
return (MakeValueResult.Cancel, default);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SetupSpinGeom()
|
|||
|
|
{
|
|||
|
|
{
|
|||
|
|
_decSpinGeom = new StreamGeometry();
|
|||
|
|
|
|||
|
|
using (var ctx = _decSpinGeom.Open())
|
|||
|
|
{
|
|||
|
|
ctx.DrawTriangle(
|
|||
|
|
new Point(0, 4),
|
|||
|
|
new Point(4, 0),
|
|||
|
|
new Point(4, 8),
|
|||
|
|
true,
|
|||
|
|
true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_decSpinGeom.Freeze();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
_incSpinGeom = new StreamGeometry();
|
|||
|
|
|
|||
|
|
using (var ctx = _incSpinGeom.Open())
|
|||
|
|
{
|
|||
|
|
ctx.DrawTriangle(
|
|||
|
|
new Point(4, 4),
|
|||
|
|
new Point(0, 0),
|
|||
|
|
new Point(0, 8),
|
|||
|
|
true,
|
|||
|
|
true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_incSpinGeom.Freeze();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private double ClampValue(double v)
|
|||
|
|
=> (v, ActualMinimum, ActualMaximum).Clamp();
|
|||
|
|
|
|||
|
|
private string FormattedValueString => Value.ToString(DisplayFormat);
|
|||
|
|
|
|||
|
|
private double SliderWidth => Math.Abs(SliderMaximum - SliderMinimum);
|
|||
|
|
|
|||
|
|
private double ActualSliderMinimum => (SliderMinimum, SliderMaximum).Min();
|
|||
|
|
|
|||
|
|
private double ActualSliderMaximum => (SliderMinimum, SliderMaximum).Max();
|
|||
|
|
|
|||
|
|
private double ActualMinimum => (Minimum, Maximum).Min();
|
|||
|
|
|
|||
|
|
private double ActualMaximum => (Minimum, Maximum).Max();
|
|||
|
|
|
|||
|
|
private string? UiValueString
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (isEditing == false)
|
|||
|
|
return Concat(FormattedValueString, UnitString);
|
|||
|
|
|
|||
|
|
var v = MakeValueFromString(textBox?.Text ?? "");
|
|||
|
|
|
|||
|
|
return
|
|||
|
|
v.Result == MakeValueResult.Ok || v.Result == MakeValueResult.Continue
|
|||
|
|
? Concat(v.Value.ToString(DisplayFormat), UnitString)
|
|||
|
|
: Concat(FormattedValueString, UnitString);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|||
|
|
private static string Concat(string a, string b)
|
|||
|
|
=> string.IsNullOrEmpty(b)
|
|||
|
|
? a
|
|||
|
|
: a + b;
|
|||
|
|
|
|||
|
|
private double UiValue
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (isEditing == false)
|
|||
|
|
return Value;
|
|||
|
|
|
|||
|
|
var v = MakeValueFromString(textBox?.Text ?? "");
|
|||
|
|
|
|||
|
|
return
|
|||
|
|
v.Result == MakeValueResult.Ok || v.Result == MakeValueResult.Continue
|
|||
|
|
? v.Value
|
|||
|
|
: Value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static StreamGeometry? _decSpinGeom;
|
|||
|
|
private static StreamGeometry? _incSpinGeom;
|
|||
|
|
|
|||
|
|
private static readonly Dictionary<long, TranslateTransform> TranslateTransformCache = new Dictionary<long, TranslateTransform>();
|
|||
|
|
}
|
|||
|
|
}
|