diff --git a/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml b/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml
index ecc21d6..437e85b 100644
--- a/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml
+++ b/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml
@@ -3,11 +3,14 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa"
+ xmlns:vm="using:Ursa.Demo.ViewModels"
+ x:DataType="vm:VerificationCodeDemoViewModel"
+ x:CompileBindings="True"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ursa.Demo.Pages.VerificationCodeDemo">
-
+
-
+
diff --git a/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml.cs b/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml.cs
index 6ac89e7..61aecec 100644
--- a/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml.cs
+++ b/demo/Ursa.Demo/Pages/VerificationCodeDemo.axaml.cs
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using Ursa.Controls;
namespace Ursa.Demo.Pages;
@@ -10,4 +11,10 @@ public partial class VerificationCodeDemo : UserControl
{
InitializeComponent();
}
+
+ private async void VerificationCode_OnComplete(object? sender, VerificationCodeCompleteEventArgs e)
+ {
+ var text = string.Join(string.Empty, e.Code);
+ await MessageBox.ShowOverlayAsync(text);
+ }
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/VerificationCodeDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/VerificationCodeDemoViewModel.cs
index 6f106e3..94e4fff 100644
--- a/demo/Ursa.Demo/ViewModels/VerificationCodeDemoViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/VerificationCodeDemoViewModel.cs
@@ -1,8 +1,26 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Avalonia.Collections;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Ursa.Controls;
namespace Ursa.Demo.ViewModels;
public class VerificationCodeDemoViewModel: ObservableObject
{
-
+ public ICommand CompleteCommand { get; set; }
+
+ public VerificationCodeDemoViewModel()
+ {
+ CompleteCommand = new AsyncRelayCommand>(OnComplete);
+ }
+
+ private async Task OnComplete(IList? obj)
+ {
+ if (obj is null) return;
+ var code = string.Join("", obj);
+ await MessageBox.ShowOverlayAsync(code);
+ }
}
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Controls/VerificationCode.axaml b/src/Ursa.Themes.Semi/Controls/VerificationCode.axaml
index 74e6993..ca051f7 100644
--- a/src/Ursa.Themes.Semi/Controls/VerificationCode.axaml
+++ b/src/Ursa.Themes.Semi/Controls/VerificationCode.axaml
@@ -1,34 +1,68 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
diff --git a/src/Ursa/Controls/VerificationCode/VerificationCode.cs b/src/Ursa/Controls/VerificationCode/VerificationCode.cs
index efbc150..cc1e9e1 100644
--- a/src/Ursa/Controls/VerificationCode/VerificationCode.cs
+++ b/src/Ursa/Controls/VerificationCode/VerificationCode.cs
@@ -28,13 +28,13 @@ public class VerificationCode: TemplatedControl
set => SetValue(CompleteCommandProperty, value);
}
- public static readonly StyledProperty CountOfDigitProperty = AvaloniaProperty.Register(
- nameof(CountOfDigit));
+ public static readonly StyledProperty CountProperty = AvaloniaProperty.Register(
+ nameof(Count));
- public int CountOfDigit
+ public int Count
{
- get => GetValue(CountOfDigitProperty);
- set => SetValue(CountOfDigitProperty, value);
+ get => GetValue(CountProperty);
+ set => SetValue(CountProperty, value);
}
public static readonly StyledProperty PasswordCharProperty =
@@ -47,19 +47,29 @@ public class VerificationCode: TemplatedControl
set => SetValue(PasswordCharProperty, value);
}
- public static readonly DirectProperty> DigitsProperty = AvaloniaProperty.RegisterDirect>(
+ public static readonly DirectProperty> DigitsProperty = AvaloniaProperty.RegisterDirect>(
nameof(Digits), o => o.Digits, (o, v) => o.Digits = v);
- private AvaloniaList _digits = [];
- internal AvaloniaList Digits
+ private IList _digits = [];
+ internal IList Digits
{
get => _digits;
set => SetAndRaise(DigitsProperty, ref _digits, value);
}
+
+ public static readonly RoutedEvent CompleteEvent =
+ RoutedEvent.Register(
+ nameof(Complete), RoutingStrategies.Bubble);
+
+ public event EventHandler Complete
+ {
+ add => AddHandler(CompleteEvent, value);
+ remove => RemoveHandler(CompleteEvent, value);
+ }
static VerificationCode()
{
- CountOfDigitProperty.Changed.AddClassHandler((code, args) => code.OnCountOfDigitChanged(args));
+ CountProperty.Changed.AddClassHandler((code, args) => code.OnCountOfDigitChanged(args));
FocusableProperty.OverrideDefaultValue(true);
}
@@ -73,9 +83,8 @@ public class VerificationCode: TemplatedControl
var newValue = args.NewValue.Value;
if (newValue > 0)
{
- Digits = new AvaloniaList(Enumerable.Repeat(string.Empty, newValue));
+ Digits = new List(Enumerable.Repeat(string.Empty, newValue));
}
-
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@@ -89,29 +98,48 @@ public class VerificationCode: TemplatedControl
{
if (e.Source is Control t)
{
- var text = t.FindLogicalAncestorOfType();
+ var text = t.FindLogicalAncestorOfType();
if (text != null)
{
+ text.Focus();
_currentIndex = _itemsControl?.IndexFromContainer(text) ?? 0;
}
}
+ e.Handled = true;
}
protected override void OnTextInput(TextInputEventArgs e)
{
base.OnTextInput(e);
- if (e.Text?.Length == 1 && _currentIndex < CountOfDigit)
+ if (e.Text?.Length == 1 && _currentIndex < Count)
{
- Digits[_currentIndex] = e.Text;
- var presenter = _itemsControl?.ContainerFromIndex(_currentIndex) as TextBox;
+
+ var presenter = _itemsControl?.ContainerFromIndex(_currentIndex) as VerificationCodeItem;
if (presenter is null) return;
- _currentIndex++;
- var newPresenter = _itemsControl?.ContainerFromIndex(_currentIndex)?.Focus();
presenter.Text = e.Text;
- if (_currentIndex == CountOfDigit)
+ Digits[_currentIndex] = e.Text;
+ _currentIndex++;
+ _itemsControl?.ContainerFromIndex(_currentIndex)?.Focus();
+ if (_currentIndex == Count)
{
CompleteCommand?.Execute(Digits);
+ RaiseEvent(new VerificationCodeCompleteEventArgs(Digits, CompleteEvent));
}
}
}
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ if (e.Key == Key.Back && _currentIndex >= 0)
+ {
+ var presenter = _itemsControl?.ContainerFromIndex(_currentIndex) as VerificationCodeItem;
+ if (presenter is null) return;
+ Digits[_currentIndex] = string.Empty;
+ presenter.Text = string.Empty;
+ if (_currentIndex == 0) return;
+ _currentIndex--;
+ _itemsControl?.ContainerFromIndex(_currentIndex)?.Focus();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Ursa/Controls/VerificationCode/VerificationCodeCollection.cs b/src/Ursa/Controls/VerificationCode/VerificationCodeCollection.cs
index 333af44..e403c87 100644
--- a/src/Ursa/Controls/VerificationCode/VerificationCodeCollection.cs
+++ b/src/Ursa/Controls/VerificationCode/VerificationCodeCollection.cs
@@ -11,14 +11,13 @@ public class VerificationCodeCollection: ItemsControl
{
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
- return NeedsContainer(item, out recycleKey);
+ return NeedsContainer(item, out recycleKey);
}
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
- return new TextBox()
+ return new VerificationCodeItem()
{
- TextAlignment = TextAlignment.Center,
[InputMethod.IsInputMethodEnabledProperty] = false,
};
}
diff --git a/src/Ursa/Controls/VerificationCode/VerificationCodeCompleteEventArgs.cs b/src/Ursa/Controls/VerificationCode/VerificationCodeCompleteEventArgs.cs
new file mode 100644
index 0000000..e1422cf
--- /dev/null
+++ b/src/Ursa/Controls/VerificationCode/VerificationCodeCompleteEventArgs.cs
@@ -0,0 +1,9 @@
+using Avalonia.Collections;
+using Avalonia.Interactivity;
+
+namespace Ursa.Controls;
+
+public class VerificationCodeCompleteEventArgs(IList code, RoutedEvent? @event) : RoutedEventArgs(@event)
+{
+ public IList Code { get; } = code;
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/VerificationCode/VerificationCodeItem.cs b/src/Ursa/Controls/VerificationCode/VerificationCodeItem.cs
new file mode 100644
index 0000000..081bc7f
--- /dev/null
+++ b/src/Ursa/Controls/VerificationCode/VerificationCodeItem.cs
@@ -0,0 +1,26 @@
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+
+namespace Ursa.Controls;
+
+public class VerificationCodeItem: TemplatedControl
+{
+ public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(
+ nameof(Text), defaultBindingMode: BindingMode.TwoWay);
+
+ public string Text
+ {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly StyledProperty PasswordCharProperty = AvaloniaProperty.Register(
+ nameof(PasswordChar), defaultBindingMode: BindingMode.TwoWay);
+
+ public char PasswordChar
+ {
+ get => GetValue(PasswordCharProperty);
+ set => SetValue(PasswordCharProperty, value);
+ }
+}
\ No newline at end of file