NumPad 修复了 官方NumericUpDown对NumPad不响应的问题

NumPad 修复了 当Target不是TextBox的时候,NumPad上的Delete和Back不响应的问题
IPv4Box 修复了 由于失去焦点所以对 NumPad 不响应的问题
This commit is contained in:
杨劼
2025-08-20 12:19:03 +08:00
parent 88318336a4
commit f9fb8cb8ae
4 changed files with 110 additions and 8 deletions

View File

@@ -21,11 +21,21 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
u:NumPad.Attach="True" u:NumPad.Attach="True"
Watermark="Invoke NumPad" /> Watermark="Invoke NumPad" />
<u:IPv4Box Width="200" u:NumPad.Attach="True" /> <u:IPv4Box
Width="200"
u:NumPad.Attach="True"
InputMode="Fast"
ShowLeadingZero="False" />
<u:NumericIntUpDown <u:NumericIntUpDown
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
u:NumPad.Attach="True" u:NumPad.Attach="True"
Watermark="Invoke NumPad" /> Watermark="Invoke u:NumericIntUpDown" />
<NumericUpDown
u:NumPad.Attach="True"
ButtonSpinnerLocation="Left"
Watermark="NumericUpDown" />
<Border Theme="{DynamicResource CardBorder}"> <Border Theme="{DynamicResource CardBorder}">
<StackPanel> <StackPanel>
<TextBox Name="text" Width="200" /> <TextBox Name="text" Width="200" />

View File

@@ -92,7 +92,7 @@
</u:NumPadButton> </u:NumPadButton>
<u:NumPadButton <u:NumPadButton
Grid.Row="1" Grid.Row="1"
Grid.RowSpan="2" Grid.RowSpan="1"
Grid.Column="3" Grid.Column="3"
Height="{x:Static x:Double.NaN}" Height="{x:Static x:Double.NaN}"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@@ -100,6 +100,16 @@
FunctionKey="Add" FunctionKey="Add"
NumContent="+" NumContent="+"
NumKey="Add" /> NumKey="Add" />
<u:NumPadButton
Grid.Row="2"
Grid.RowSpan="1"
Grid.Column="3"
Height="{x:Static x:Double.NaN}"
VerticalAlignment="Stretch"
FunctionContent="←"
FunctionKey="Back"
NumContent="←"
NumKey="Back" />
<u:NumPadButton <u:NumPadButton
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"

View File

@@ -108,6 +108,24 @@ public class IPv4Box: TemplatedControl
}); });
} }
/// <summary>
/// <20>Ƿ<EFBFBD>ʹ<EFBFBD><CAB9>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
internal bool IsTargetByNumPad
{
set
{
if(_isTargetByNumPad
&&!value)
{
SetLostFocus();
}
_isTargetByNumPad =value;
}
get => _isTargetByNumPad;
}
private bool _isTargetByNumPad;
#region Overrides #region Overrides
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
@@ -308,6 +326,15 @@ public class IPv4Box: TemplatedControl
protected override void OnLostFocus(RoutedEventArgs e) protected override void OnLostFocus(RoutedEventArgs e)
{ {
base.OnLostFocus(e); base.OnLostFocus(e);
if (IsTargetByNumPad)
{
return;
}
SetLostFocus();
}
private void SetLostFocus()
{
foreach (var pre in _presenters) foreach (var pre in _presenters)
{ {
pre?.HideCaret(); pre?.HideCaret();
@@ -318,6 +345,7 @@ public class IPv4Box: TemplatedControl
SetIPAddressInternal(); SetIPAddressInternal();
} }
protected override void OnGotFocus(GotFocusEventArgs e) protected override void OnGotFocus(GotFocusEventArgs e)
{ {
_currentActivePresenter = _firstText; _currentActivePresenter = _firstText;

View File

@@ -1,6 +1,9 @@
using Avalonia; using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.LogicalTree;
using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -16,6 +19,11 @@ public class NumPad: TemplatedControl
set => SetValue(TargetProperty, value); set => SetValue(TargetProperty, value);
} }
/// <summary>
/// Target Ŀ<><C4BF><EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD> TextBox <20><> TextPresenter <20>ؼ<EFBFBD>
/// </summary>
private TextBox? _targetInnerText;
public static readonly StyledProperty<bool> NumModeProperty = AvaloniaProperty.Register<NumPad, bool>( public static readonly StyledProperty<bool> NumModeProperty = AvaloniaProperty.Register<NumPad, bool>(
nameof(NumMode), defaultValue: true); nameof(NumMode), defaultValue: true);
@@ -54,10 +62,24 @@ public class NumPad: TemplatedControl
var existing = OverlayDialog.Recall<NumPad>(null); var existing = OverlayDialog.Recall<NumPad>(null);
if (existing is not null) if (existing is not null)
{ {
if(existing.Target is IPv4Box pv4Box)
{
pv4Box.IsTargetByNumPad = false; // ȡ<><C8A1> IPv4Box <20><> NumPad <20><><EFBFBD><EFBFBD>ģʽ
}
existing.Target = sender as InputElement; existing.Target = sender as InputElement;
existing._targetInnerText = FindTextBoxInTarget((sender as InputElement)!);
if (existing.Target is IPv4Box pv4Box2)
{
pv4Box2.IsTargetByNumPad = true;
}
return; return;
} }
var numPad = new NumPad() { Target = sender as InputElement }; var numPad = new NumPad()
{
Target = sender as InputElement ,
_targetInnerText = FindTextBoxInTarget((sender as InputElement)!)
};
OverlayDialog.Show(numPad, new object(), options: new OverlayDialogOptions() { Buttons = DialogButton.None }); OverlayDialog.Show(numPad, new object(), options: new OverlayDialogOptions() { Buttons = DialogButton.None });
} }
@@ -84,9 +106,12 @@ public class NumPad: TemplatedControl
{ {
if (Target is null || o is not NumPadButton b) return; if (Target is null || o is not NumPadButton b) return;
var key = (b.NumMode ? b.NumKey : b.FunctionKey)?? Key.None; var key = (b.NumMode ? b.NumKey : b.FunctionKey)?? Key.None;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD>Ϊ TextBox <20><>Ŀ<EFBFBD><C4BF><EFBFBD>ؼ<EFBFBD><D8BC><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>ø<EFBFBD> TextBox <20><>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>
var realTarget = _targetInnerText ?? Target;
if (KeyInputMapping.TryGetValue(key, out var s)) if (KeyInputMapping.TryGetValue(key, out var s))
{ {
Target.RaiseEvent(new TextInputEventArgs() realTarget.RaiseEvent(new TextInputEventArgs()
{ {
Source = this, Source = this,
RoutedEvent = TextInputEvent, RoutedEvent = TextInputEvent,
@@ -95,7 +120,7 @@ public class NumPad: TemplatedControl
} }
else else
{ {
Target.RaiseEvent(new KeyEventArgs() realTarget.RaiseEvent(new KeyEventArgs()
{ {
Source = this, Source = this,
RoutedEvent = KeyDownEvent, RoutedEvent = KeyDownEvent,
@@ -103,4 +128,33 @@ public class NumPad: TemplatedControl
}); });
} }
} }
/// <summary>
/// <20><>Ŀ<EFBFBD><C4BF><EFBFBD>ؼ<EFBFBD><D8BC>в<EFBFBD><D0B2><EFBFBD> TextBox <20>ؼ<EFBFBD>
/// </summary>
/// <param name="target">Ŀ<><C4BF><EFBFBD>ؼ<EFBFBD></param>
/// <returns><3E>ҵ<EFBFBD><D2B5><EFBFBD> TextBox<6F><78><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD><D2B5>򷵻<EFBFBD> null</returns>
private static TextBox? FindTextBoxInTarget(InputElement target)
{
// <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><EFBFBD><EAB1BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD> TextBox
if (target is TextBox textBox)
return textBox;
// <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD> TemplatedControl<6F><6C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ѿ<EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>
if (target is TemplatedControl templatedControl && templatedControl.IsInitialized)
{
// <20><><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD> PART_TextBox
if (templatedControl.GetTemplateChildren().FirstOrDefault(c => c is TextBox) is TextBox partTextBox)
return partTextBox;
}
// <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD> ILogical<61><6C>ʹ<EFBFBD><CAB9> LogicalTree <20><>չ<EFBFBD><D5B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (target is ILogical logical)
{
// ʹ<><CAB9> GetLogicalDescendants <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD><DFBC>ӿؼ<D3BF>
var textBoxes = logical.GetLogicalDescendants().OfType<TextBox>();
return textBoxes.FirstOrDefault();
}
return null;
}
} }