Advertisement
bobmarley12345

Adobe "numeric text slider" for WPF

May 28th, 2023
1,375
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.53 KB | None | 0 0
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Controls.Primitives;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Threading;
  10. using FrameControlEx.Core.Utils;
  11.  
  12. namespace FrameControlEx.Controls {
  13.     [TemplatePart(Name = "PART_TextBlock", Type = typeof(TextBlock))]
  14.     [TemplatePart(Name = "PART_TextBox", Type = typeof(TextBox))]
  15.     public class NumberDragger : RangeBase {
  16.         public static readonly DependencyProperty VerySmallChangeProperty =
  17.             DependencyProperty.Register(
  18.                 "VerySmallChange",
  19.                 typeof(double),
  20.                 typeof(NumberDragger),
  21.                 new PropertyMetadata(0.01d));
  22.  
  23.         public static readonly DependencyProperty NormalChangeProperty =
  24.             DependencyProperty.Register(
  25.                 "NormalChange",
  26.                 typeof(double),
  27.                 typeof(NumberDragger),
  28.                 new PropertyMetadata(1d));
  29.  
  30.         protected static readonly DependencyPropertyKey IsDraggingPropertyKey =
  31.             DependencyProperty.RegisterReadOnly(
  32.                 "IsDragging",
  33.                 typeof(bool),
  34.                 typeof(NumberDragger),
  35.                 new PropertyMetadata(BoolBox.False,
  36.                     (d, e) => ((NumberDragger) d).OnIsDraggingChanged((bool) e.OldValue, (bool) e.NewValue)));
  37.  
  38.         public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty;
  39.  
  40.         public static readonly DependencyProperty OrientationProperty =
  41.             DependencyProperty.Register(
  42.                 "Orientation",
  43.                 typeof(Orientation),
  44.                 typeof(NumberDragger),
  45.                 new PropertyMetadata(Orientation.Horizontal,
  46.                     (d, e) => ((NumberDragger) d).OnOrientationChanged((Orientation) e.OldValue, (Orientation) e.NewValue)));
  47.  
  48.         public static readonly DependencyPropertyKey IsEditingTextBoxPropertyKey =
  49.             DependencyProperty.RegisterReadOnly(
  50.                 "IsEditingTextBox",
  51.                 typeof(bool),
  52.                 typeof(NumberDragger),
  53.                 new PropertyMetadata(BoolBox.False,
  54.                     (d, e) => ((NumberDragger) d).OnIsEditingTextBoxChanged((bool) e.OldValue, (bool) e.NewValue),
  55.                     (d, v) => ((NumberDragger) d).OnCoerceIsEditingTextBox((bool) v)));
  56.  
  57.         public static readonly DependencyProperty IsEditingTextBoxProperty = IsEditingTextBoxPropertyKey.DependencyProperty;
  58.  
  59.         public static readonly DependencyProperty RoundedPlacesProperty =
  60.             DependencyProperty.Register(
  61.                 "RoundedPlaces",
  62.                 typeof(int),
  63.                 typeof(NumberDragger),
  64.                 new PropertyMetadata(4,
  65.                     (d, e) => ((NumberDragger) d).OnRoundedPlacesChanged((int) e.OldValue, (int) e.NewValue)));
  66.  
  67.         public static readonly DependencyProperty LockCursorWhileDraggingProperty =
  68.             DependencyProperty.Register(
  69.                 "LockCursorWhileDragging",
  70.                 typeof(bool),
  71.                 typeof(NumberDragger),
  72.                 new PropertyMetadata(BoolBox.False, (d, e) => throw new NotImplementedException("Locking the mouse cursor is currently unsupported")));
  73.  
  74.         private TextBlock PART_TextBlock;
  75.         private TextBox PART_TextBox;
  76.         private Point? lastClickPoint;
  77.         private Point? lastMouseMove;
  78.         private double? previousValue;
  79.         private bool ignoreMouseMove;
  80.  
  81.         public double VerySmallChange {
  82.             get => (double) this.GetValue(VerySmallChangeProperty);
  83.             set => this.SetValue(VerySmallChangeProperty, value);
  84.         }
  85.  
  86.         /// <summary>
  87.         /// The amount to add per pixel of change while dragging
  88.         /// </summary>
  89.         public double NormalChange {
  90.             get => (double) this.GetValue(NormalChangeProperty);
  91.             set => this.SetValue(NormalChangeProperty, value);
  92.         }
  93.  
  94.         public bool IsDragging {
  95.             get => (bool) this.GetValue(IsDraggingProperty);
  96.             protected set => this.SetValue(IsDraggingPropertyKey, value.Box());
  97.         }
  98.  
  99.         public Orientation Orientation {
  100.             get => (Orientation) this.GetValue(OrientationProperty);
  101.             set => this.SetValue(OrientationProperty, value);
  102.         }
  103.  
  104.         public bool IsEditingTextBox {
  105.             get => (bool) this.GetValue(IsEditingTextBoxProperty);
  106.             protected set => this.SetValue(IsEditingTextBoxPropertyKey, value.Box());
  107.         }
  108.  
  109.         public int RoundedPlaces {
  110.             get => (int) this.GetValue(RoundedPlacesProperty);
  111.             set => this.SetValue(RoundedPlacesProperty, value);
  112.         }
  113.  
  114.         private bool isUpdatingExternalMouse;
  115.  
  116.         public bool LockCursorWhileDragging {
  117.             get => (bool) this.GetValue(LockCursorWhileDraggingProperty);
  118.             set => this.SetValue(LockCursorWhileDraggingProperty, value.Box());
  119.         }
  120.  
  121.         public double RoundedValue => this.GetRoundedValue(this.Value);
  122.  
  123.         public NumberDragger() {
  124.             this.SmallChange = 0.1d;
  125.             this.LargeChange = 2.5d;
  126.             this.Loaded += (s, e) => {
  127.                 this.CoerceValue(IsEditingTextBoxProperty);
  128.                 this.UpdateText();
  129.             };
  130.         }
  131.  
  132.         public double GetRoundedValue(double value) {
  133.             return Math.Round(value, this.RoundedPlaces);
  134.         }
  135.  
  136.         protected virtual void OnIsDraggingChanged(bool oldValue, bool newValue) {
  137.  
  138.         }
  139.  
  140.         protected virtual void OnOrientationChanged(Orientation oldValue, Orientation newValue) {
  141.             if (this.IsDragging) {
  142.                 this.CancelDrag();
  143.             }
  144.  
  145.             this.IsEditingTextBox = false;
  146.         }
  147.  
  148.         protected virtual void OnIsEditingTextBoxChanged(bool oldValue, bool newValue) {
  149.             if (newValue && this.IsDragging) {
  150.                 this.CancelDrag();
  151.             }
  152.  
  153.             this.UpdateText();
  154.             if (oldValue != newValue) {
  155.                 this.PART_TextBox.Focus();
  156.                 this.PART_TextBox.SelectAll();
  157.             }
  158.         }
  159.  
  160.         private bool OnCoerceIsEditingTextBox(bool isEditing) {
  161.             if (this.PART_TextBox == null || this.PART_TextBlock == null) {
  162.                 return isEditing;
  163.             }
  164.  
  165.             if (isEditing) {
  166.                 this.PART_TextBox.Visibility = Visibility.Visible;
  167.                 this.PART_TextBlock.Visibility = Visibility.Hidden;
  168.             }
  169.             else {
  170.                 this.PART_TextBox.Visibility = Visibility.Hidden;
  171.                 this.PART_TextBlock.Visibility = Visibility.Visible;
  172.             }
  173.  
  174.             this.UpdateCursor();
  175.             return isEditing;
  176.         }
  177.  
  178.         public void UpdateCursor() {
  179.             Cursor cursor;
  180.             switch (this.Orientation) {
  181.                 case Orientation.Horizontal:
  182.                     cursor = Cursors.SizeWE;
  183.                     break;
  184.                 case Orientation.Vertical:
  185.                     cursor = Cursors.SizeNS;
  186.                     break;
  187.                 default:
  188.                     cursor = Cursors.Arrow;
  189.                     break;
  190.             }
  191.  
  192.             if (this.IsDragging || this.IsMouseOver) {
  193.                 this.Cursor = cursor;
  194.                 this.PART_TextBlock.ClearValue(CursorProperty);
  195.             }
  196.             else {
  197.                 this.ClearValue(CursorProperty);
  198.                 if (this.IsEditingTextBox) {
  199.                     this.PART_TextBlock.ClearValue(CursorProperty);
  200.                 }
  201.                 else {
  202.                     this.PART_TextBlock.Cursor = cursor;
  203.                 }
  204.             }
  205.         }
  206.  
  207.         protected virtual void OnRoundedPlacesChanged(int? oldValue, int? newValue) {
  208.             if (newValue != null) {
  209.                 this.UpdateText();
  210.             }
  211.         }
  212.  
  213.         protected override void OnValueChanged(double oldValue, double newValue) {
  214.             base.OnValueChanged(oldValue, newValue);
  215.             this.UpdateText();
  216.         }
  217.  
  218.         protected void UpdateText() {
  219.             string text = this.RoundedValue.ToString();
  220.             if (this.IsEditingTextBox) {
  221.                 if (this.PART_TextBox == null)
  222.                     return;
  223.                 this.PART_TextBox.Text = text;
  224.             }
  225.             else {
  226.                 if (this.PART_TextBlock == null)
  227.                     return;
  228.                 this.PART_TextBlock.Text = text;
  229.             }
  230.         }
  231.  
  232.         public override void OnApplyTemplate() {
  233.             base.OnApplyTemplate();
  234.             this.PART_TextBlock = this.GetTemplateChild("PART_TextBlock") as TextBlock ?? throw new Exception("Missing template part: " + nameof(this.PART_TextBlock));
  235.             this.PART_TextBox = this.GetTemplateChild("PART_TextBox") as TextBox ?? throw new Exception("Missing template part: " + nameof(this.PART_TextBox));
  236.             this.PART_TextBox.Focusable = true;
  237.             this.PART_TextBox.KeyDown += this.OnTextBoxKeyDown;
  238.             this.PART_TextBox.GotFocus += (s, e) => {
  239.                 if (this.PART_TextBox.IsFocused || this.PART_TextBox.IsMouseCaptured) {
  240.                     this.IsEditingTextBox = true;
  241.                 }
  242.             };
  243.  
  244.             this.PART_TextBox.LostFocus += (s, e) => {
  245.                 this.IsEditingTextBox = false;
  246.             };
  247.  
  248.             this.CoerceValue(IsEditingTextBoxProperty);
  249.         }
  250.  
  251.         protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
  252.             if (!this.IsDragging) {
  253.                 e.Handled = true;
  254.                 this.Focus();
  255.  
  256.                 this.ignoreMouseMove = true;
  257.                 try {
  258.                     this.CaptureMouse();
  259.                     Debug.WriteLine("Mouse Captured");
  260.                 }
  261.                 finally {
  262.                     this.ignoreMouseMove = false;
  263.                 }
  264.  
  265.                 this.lastMouseMove = this.lastClickPoint = e.GetPosition(this);
  266.                 this.UpdateCursor();
  267.             }
  268.  
  269.             base.OnMouseLeftButtonDown(e);
  270.         }
  271.  
  272.         protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) {
  273.             e.Handled = true;
  274.             if (this.IsDragging) {
  275.                 this.CompleteDrag();
  276.             }
  277.             else if (this.IsMouseOver) {
  278.                 if (this.IsMouseCaptured) {
  279.                     this.ReleaseMouseCapture();
  280.                     Debug.WriteLine("Mouse Capture Released");
  281.                 }
  282.  
  283.                 this.IsEditingTextBox = true;
  284.                 this.UpdateCursor();
  285.             }
  286.  
  287.             base.OnMouseLeftButtonUp(e);
  288.         }
  289.  
  290.         protected override void OnMouseMove(MouseEventArgs e) {
  291.             base.OnMouseMove(e);
  292.             if (this.ignoreMouseMove || this.isUpdatingExternalMouse) {
  293.                 return;
  294.             }
  295.  
  296.             if (this.IsEditingTextBox) {
  297.                 if (this.IsDragging) {
  298.                     Debug.WriteLine("IsDragging and IsEditingTextBox were both true");
  299.                     this.previousValue = null;
  300.                     this.CancelDrag();
  301.                 }
  302.  
  303.                 return;
  304.             }
  305.  
  306.             if (e.LeftButton != MouseButtonState.Pressed) {
  307.                 if (this.IsDragging) {
  308.                     this.CompleteDrag();
  309.                 }
  310.  
  311.                 return;
  312.             }
  313.  
  314.             if (Keyboard.IsKeyDown(Key.Escape) && this.IsDragging) {
  315.                 this.CancelDrag();
  316.                 return;
  317.             }
  318.  
  319.             Point mouse = e.GetPosition(this);
  320.             if (this.lastClickPoint is Point lastClick && !this.IsDragging) {
  321.                 if (Math.Abs(mouse.X - lastClick.X) < 5d && Math.Abs(mouse.Y - lastClick.Y) < 5d) {
  322.                     return;
  323.                 }
  324.  
  325.                 this.BeginMouseDrag();
  326.             }
  327.  
  328.             if (!this.IsDragging) {
  329.                 return;
  330.             }
  331.  
  332.             if (this.IsEditingTextBox) {
  333.                 Debug.WriteLine("IsEditingTextBox and IsDragging were both true");
  334.                 this.IsEditingTextBox = false;
  335.             }
  336.  
  337.             if (!(this.lastMouseMove is Point lastMouse)) {
  338.                 return;
  339.             }
  340.  
  341.             double change;
  342.             switch (this.Orientation) {
  343.                 case Orientation.Horizontal: {
  344.                     change = mouse.X - lastMouse.X;
  345.                     break;
  346.                 }
  347.                 case Orientation.Vertical: {
  348.                     change = mouse.Y - lastMouse.Y;
  349.                     break;
  350.                 }
  351.                 default: {
  352.                     throw new Exception("Invalid orientation: " + this.Orientation);
  353.                 }
  354.             }
  355.  
  356.             bool isShiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
  357.             bool isCtrlDown = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
  358.  
  359.             if (isShiftDown) {
  360.                 if (isCtrlDown) {
  361.                     change *= this.VerySmallChange;
  362.                 }
  363.                 else {
  364.                     change *= this.SmallChange;
  365.                 }
  366.             }
  367.             else if (isCtrlDown) {
  368.                 change *= this.LargeChange;
  369.             }
  370.             else {
  371.                 change *= this.NormalChange;
  372.             }
  373.  
  374.             double roundedValue = Maths.Clamp(this.GetRoundedValue(this.Value + change), this.Minimum, this.Maximum);
  375.             if (Maths.Equals(this.RoundedValue, roundedValue)) {
  376.                 return;
  377.             }
  378.  
  379.             this.Value = roundedValue;
  380.             this.lastMouseMove = mouse;
  381.         }
  382.  
  383.         protected override void OnKeyDown(KeyEventArgs e) {
  384.             base.OnKeyDown(e);
  385.             if (!this.IsDragging || e.Key != Key.Escape) {
  386.                 return;
  387.             }
  388.  
  389.             e.Handled = true;
  390.             this.CancelInputEdit();
  391.             if (this.IsDragging) {
  392.                 this.CancelDrag();
  393.             }
  394.  
  395.             this.IsEditingTextBox = false;
  396.         }
  397.  
  398.         private void OnTextBoxKeyDown(object sender, KeyEventArgs e) {
  399.             if (!this.IsDragging && this.IsEditingTextBox) {
  400.                 if (e.Key != Key.Enter && e.Key != Key.Escape) {
  401.                     return;
  402.                 }
  403.  
  404.                 if (e.Key == Key.Enter && double.TryParse(this.PART_TextBox.Text, out double value)) {
  405.                     this.CompleteInputEdit(value);
  406.                 }
  407.                 else {
  408.                     this.CancelInputEdit();
  409.                 }
  410.             }
  411.         }
  412.  
  413.         protected override void OnLostFocus(RoutedEventArgs e) {
  414.             base.OnLostFocus(e);
  415.             if (this.IsDragging) {
  416.                 this.CancelDrag();
  417.             }
  418.  
  419.             this.IsEditingTextBox = false;
  420.         }
  421.  
  422.         public void CompleteInputEdit(double value) {
  423.             this.IsEditingTextBox = false;
  424.             this.Value = value;
  425.             this.UpdateCursor();
  426.         }
  427.  
  428.         public void CancelInputEdit() {
  429.             this.IsEditingTextBox = false;
  430.             this.UpdateText();
  431.             this.UpdateCursor();
  432.         }
  433.  
  434.         public void BeginMouseDrag() {
  435.             this.IsEditingTextBox = false;
  436.             this.previousValue = this.Value;
  437.             this.Focus();
  438.             this.CaptureMouse();
  439.             Debug.WriteLine("[BeginMouseDrag] Mouse Captured");
  440.             this.IsDragging = true;
  441.             this.UpdateCursor();
  442.         }
  443.  
  444.         public void CompleteDrag() {
  445.             this.CleanUpDrag();
  446.             this.previousValue = null;
  447.         }
  448.  
  449.         public void CancelDrag() {
  450.             this.CleanUpDrag();
  451.             if (this.previousValue is double oldVal) {
  452.                 this.previousValue = null;
  453.                 this.Value = oldVal;
  454.             }
  455.         }
  456.  
  457.         protected void CleanUpDrag() {
  458.             if (!this.IsDragging)
  459.                 return;
  460.             if (this.IsMouseCaptured)
  461.                 this.ReleaseMouseCapture();
  462.             this.ClearValue(IsDraggingPropertyKey);
  463.  
  464.             this.lastMouseMove = null;
  465.             this.lastClickPoint = null;
  466.  
  467.             this.UpdateCursor();
  468.         }
  469.     }
  470.  
  471.     // internal static class MouseUtils {
  472.     //     [DllImport("user32.dll")]
  473.     //     public static extern bool SetCursorPos(int x, int y);
  474.     //
  475.     //     public static void SetCursorPos(Point position) {
  476.     //         SetCursorPos((int) position.X, (int) position.Y);
  477.     //     }
  478.     // }
  479. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement