csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
9.4 KiB
291 lines
9.4 KiB
using Avalonia.Animation;
|
|
using Avalonia.Controls.Metadata;
|
|
using Avalonia.Controls.Presenters;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Controls.Templates;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.LogicalTree;
|
|
|
|
namespace Avalonia.Controls
|
|
{
|
|
/// <summary>
|
|
/// A Toggle Switch control.
|
|
/// </summary>
|
|
[TemplatePart("PART_MovingKnobs", typeof(Panel))]
|
|
[TemplatePart("PART_OffContentPresenter", typeof(ContentPresenter))]
|
|
[TemplatePart("PART_OnContentPresenter", typeof(ContentPresenter))]
|
|
[TemplatePart("PART_SwitchKnob", typeof(Panel))]
|
|
[PseudoClasses(":dragging")]
|
|
public class ToggleSwitch : ToggleButton
|
|
{
|
|
private Panel? _knobsPanel;
|
|
private Panel? _switchKnob;
|
|
private bool _knobsPanelPressed = false;
|
|
private Point _switchStartPoint = new Point();
|
|
private double _initLeft = -1;
|
|
private bool _isDragging = false;
|
|
|
|
static ToggleSwitch()
|
|
{
|
|
OffContentProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) => x.OffContentChanged(e));
|
|
OnContentProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) => x.OnContentChanged(e));
|
|
IsCheckedProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) =>
|
|
{
|
|
if ((e.NewValue != null) && (e.NewValue is bool val))
|
|
{
|
|
x.UpdateKnobPos(val);
|
|
}
|
|
});
|
|
|
|
BoundsProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) =>
|
|
{
|
|
if (x.IsChecked != null)
|
|
{
|
|
x.UpdateKnobPos(x.IsChecked.Value);
|
|
}
|
|
});
|
|
KnobTransitionsProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) =>
|
|
{
|
|
x.UpdateKnobTransitions();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="OffContent"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<object?> OffContentProperty =
|
|
AvaloniaProperty.Register<ToggleSwitch, object?>(nameof(OffContent), defaultValue: "Off");
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="OffContentTemplate"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<IDataTemplate?> OffContentTemplateProperty =
|
|
AvaloniaProperty.Register<ToggleSwitch, IDataTemplate?>(nameof(OffContentTemplate));
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="OnContent"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<object?> OnContentProperty =
|
|
AvaloniaProperty.Register<ToggleSwitch, object?>(nameof(OnContent), defaultValue: "On");
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="OnContentTemplate"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<IDataTemplate?> OnContentTemplateProperty =
|
|
AvaloniaProperty.Register<ToggleSwitch, IDataTemplate?>(nameof(OnContentTemplate));
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="KnobTransitions"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<Transitions> KnobTransitionsProperty =
|
|
AvaloniaProperty.Register<ToggleSwitch, Transitions>(nameof(KnobTransitions));
|
|
|
|
/// <summary>
|
|
/// Gets or Sets the Content that is displayed when in the On State.
|
|
/// </summary>
|
|
public object? OnContent
|
|
{
|
|
get { return GetValue(OnContentProperty); }
|
|
set { SetValue(OnContentProperty, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or Sets the Content that is displayed when in the Off State.
|
|
/// </summary>
|
|
public object? OffContent
|
|
{
|
|
get { return GetValue(OffContentProperty); }
|
|
set { SetValue(OffContentProperty, value); }
|
|
}
|
|
|
|
public ContentPresenter? OffContentPresenter
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public ContentPresenter? OnContentPresenter
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or Sets the <see cref="IDataTemplate"/> used to display the <see cref="OffContent"/>.
|
|
/// </summary>
|
|
public IDataTemplate? OffContentTemplate
|
|
{
|
|
get { return GetValue(OffContentTemplateProperty); }
|
|
set { SetValue(OffContentTemplateProperty, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or Sets the <see cref="IDataTemplate"/> used to display the <see cref="OnContent"/>.
|
|
/// </summary>
|
|
public IDataTemplate? OnContentTemplate
|
|
{
|
|
get { return GetValue(OnContentTemplateProperty); }
|
|
set { SetValue(OnContentTemplateProperty, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or Sets the <see cref="Transitions"/> of switching knob.
|
|
/// </summary>
|
|
public Transitions KnobTransitions
|
|
{
|
|
get { return GetValue(KnobTransitionsProperty); }
|
|
set { SetValue(KnobTransitionsProperty, value); }
|
|
}
|
|
|
|
|
|
|
|
private void OffContentChanged(AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if (e.OldValue is ILogical oldChild)
|
|
{
|
|
LogicalChildren.Remove(oldChild);
|
|
}
|
|
|
|
if (e.NewValue is ILogical newChild)
|
|
{
|
|
LogicalChildren.Add(newChild);
|
|
}
|
|
}
|
|
|
|
private void OnContentChanged(AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if (e.OldValue is ILogical oldChild)
|
|
{
|
|
LogicalChildren.Remove(oldChild);
|
|
}
|
|
|
|
if (e.NewValue is ILogical newChild)
|
|
{
|
|
LogicalChildren.Add(newChild);
|
|
}
|
|
}
|
|
|
|
protected override bool RegisterContentPresenter(ContentPresenter presenter)
|
|
{
|
|
var result = base.RegisterContentPresenter(presenter);
|
|
|
|
if (presenter.Name == "Part_OnContentPresenter")
|
|
{
|
|
OnContentPresenter = presenter;
|
|
result = true;
|
|
}
|
|
else if (presenter.Name == "PART_OffContentPresenter")
|
|
{
|
|
OffContentPresenter = presenter;
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
{
|
|
base.OnApplyTemplate(e);
|
|
|
|
_switchKnob = e.NameScope.Find<Panel>("PART_SwitchKnob");
|
|
_knobsPanel = e.NameScope.Get<Panel>("PART_MovingKnobs");
|
|
|
|
_knobsPanel.PointerPressed += KnobsPanel_PointerPressed;
|
|
_knobsPanel.PointerReleased += KnobsPanel_PointerReleased;
|
|
_knobsPanel.PointerMoved += KnobsPanel_PointerMoved;
|
|
|
|
if (IsChecked.HasValue)
|
|
{
|
|
UpdateKnobPos(IsChecked.Value);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
protected override void OnLoaded(RoutedEventArgs e)
|
|
{
|
|
base.OnLoaded(e);
|
|
UpdateKnobTransitions();
|
|
}
|
|
|
|
private void UpdateKnobTransitions()
|
|
{
|
|
if (_knobsPanel != null)
|
|
{
|
|
_knobsPanel.Transitions = KnobTransitions;
|
|
}
|
|
}
|
|
|
|
private void KnobsPanel_PointerPressed(object? sender, Input.PointerPressedEventArgs e)
|
|
{
|
|
_switchStartPoint = e.GetPosition(_switchKnob);
|
|
_initLeft = Canvas.GetLeft(_knobsPanel!);
|
|
_isDragging = false;
|
|
_knobsPanelPressed = true;
|
|
}
|
|
|
|
private void KnobsPanel_PointerReleased(object? sender, Input.PointerReleasedEventArgs e)
|
|
{
|
|
if (_isDragging)
|
|
{
|
|
bool shouldBecomeChecked = Canvas.GetLeft(_knobsPanel!) >= (_switchKnob!.Bounds.Width / 2);
|
|
_knobsPanel!.ClearValue(Canvas.LeftProperty);
|
|
|
|
PseudoClasses.Set(":dragging", false);
|
|
|
|
if (shouldBecomeChecked == IsChecked)
|
|
{
|
|
UpdateKnobPos(shouldBecomeChecked);
|
|
}
|
|
else
|
|
{
|
|
SetCurrentValue(IsCheckedProperty, shouldBecomeChecked);
|
|
}
|
|
UpdateKnobTransitions();
|
|
}
|
|
|
|
_isDragging = false;
|
|
|
|
_knobsPanelPressed = false;
|
|
}
|
|
|
|
private void KnobsPanel_PointerMoved(object? sender, Input.PointerEventArgs e)
|
|
{
|
|
if (_knobsPanelPressed)
|
|
{
|
|
if(_knobsPanel != null)
|
|
{
|
|
_knobsPanel.Transitions = null;
|
|
}
|
|
var difference = e.GetPosition(_switchKnob) - _switchStartPoint;
|
|
|
|
if ((!_isDragging) && (System.Math.Abs(difference.X) > 3))
|
|
{
|
|
_isDragging = true;
|
|
PseudoClasses.Set(":dragging", true);
|
|
}
|
|
|
|
if (_isDragging)
|
|
{
|
|
Canvas.SetLeft(_knobsPanel!, System.Math.Min(_switchKnob!.Bounds.Width, System.Math.Max(0, (_initLeft + difference.X))));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateKnobPos(bool value)
|
|
{
|
|
if ((_switchKnob != null) && (_knobsPanel != null))
|
|
{
|
|
if (value)
|
|
{
|
|
Canvas.SetLeft(_knobsPanel, _switchKnob.Bounds.Width);
|
|
}
|
|
else
|
|
{
|
|
Canvas.SetLeft(_knobsPanel, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|