概述
UWP Community Toolkit Extensions 中有一个为TextBox 提供的 SurfaceDial 扩展 - SurfaceDialTextbox,本篇我们结合代码详细讲解 SurfaceDialTextbox 的实现。
SurfaceDialTextbox 为 TextBox 提供了一种简单的 Surface Dial 的菜单和操作方式,支持设置 TextBox 操作在 Dial 中的圆形菜单,选择后旋转 Dial 可以方便的对 TextBox 中的数值进行调整,调整方式类似于 NumericUpdown,可以设置数值上限和下限,以及步长;旋转 Dial 可以选择有震动反馈,超过界限后也会有反馈提示,点按 Dial 可以选择 Focus 到下一个 Element。接下来看看官方示例的截图:
Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/extensions/surfacedialtextboxhelper
Namespace: Microsoft.Toolkit.Uwp.UI.Extensions; Nuget: Microsoft.Toolkit.Uwp.UI;
开发过程
代码分析
SurfaceDialTextbox 的处理在类 SurfaceDialTextbox.cs 中,我们先来看看类的结构:
可以看到类中定义了如下的依赖属性:
然后我们看几个全局静态变量:
接下来看几个 SurfaceDialTextbox 对应的主要处理方法:
1. Controller_RotationChanged(sender, args)
Surface Dial 旋转时触发的事件处理方法,根据当前显示的 Text,获取对应的 double 类型值;根据当前 Dial 旋转角度和 StepValue 来判断数值的变化,如果允许最大最小值区间限制,则判断是否越界后设置数值;如果 Text 不是数值,则设置默认值 0.0;
private static void Controller_RotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args)
{
if (_textBox == null)
{
return;
}
string t = _textBox.Text;
double nr;
if (double.TryParse(t, out nr))
{
nr += args.RotationDeltaInDegrees * GetStepValue(_textBox);
if (GetEnableMinMaxValue(_textBox))
{
if (nr < GetMinValue(_textBox))
{
nr = GetMinValue(_textBox);
}
if (nr > GetMaxValue(_textBox))
{
nr = GetMaxValue(_textBox);
}
}
}
else
{
// default to zero if content is not a number
nr = 0.0d;
}
_textBox.Text = nr.ToString("0.00");
}
2. StepValueChanged(d, e)
StepValue 属性改变时触发事件的处理逻,为 textBox 设置 GotFocus 和 LostFocus 事件绑定,我们来看看这两个事件的处理:
TextBox_GotFocus(sender, e) 处理逻辑:获取 Radial Controller,如果需要添加 MenuItem,则根据设置添加对应的 MenuItem;设置转动时的震动反馈,旋转的步长,旋转变化事件;如果允许点击 Focus 到下一个控件,则设置 Radial Controller 的 点击事件;
private static void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
_textBox = sender as TextBox;
if (_textBox == null)
{
return;
}
if (!IsSupported)
{
return;
}
_controller = _controller ?? RadialController.CreateForCurrentView();
if (GetForceMenuItem(_textBox))
{
_stepTextMenuItem = RadialControllerMenuItem.CreateFromKnownIcon("Step Text Box", GetIcon(_textBox));
_controller.Menu.Items.Add(_stepTextMenuItem);
_controller.Menu.SelectMenuItem(_stepTextMenuItem);
}
_controller.UseAutomaticHapticFeedback = GetEnableHapticFeedback(_textBox);
_controller.RotationResolutionInDegrees = 1;
_controller.RotationChanged += Controller_RotationChanged;
if (GetEnableTapToNextControl(_textBox))
{
_controller.ButtonClicked += Controller_ButtonClicked;
}
}
TextBox_LostFocus(sender, e) GotFocus 事件对应的相反处理,作用是在 Radial Controller menu 中去掉对应的 MenuItem;解除 RotationChanged 和 Controller_ButtonClicked 事件的绑定;
private static void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (_textBox == null)
{
return;
}
if (GetForceMenuItem(_textBox))
{
_controller.Menu.Items.Remove(_stepTextMenuItem);
}
_controller.RotationChanged -= Controller_RotationChanged;
if (GetEnableTapToNextControl(_textBox))
{
_controller.ButtonClicked -= Controller_ButtonClicked;
}
_textBox = null;
}
调用示例
我们设置一个 SurfaceDialTextbox,默认值为 0,每次变化为 1,区间是 0~100;来看第一张图中,点按 Dial 时会出现 SurfaceDialTextbox 的菜单,菜单图标是尺子;点按后可以通过旋转来改变 textbox 的值;如果初始时不点按 Dial,直接旋转也是一样改变 textbox 的值;在值超出 0~100 区间时,Dial 会有震动反馈;
<TextBox HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,0,0,10"
Text="0"
Width="250" Height="50" FontSize="20"
extensions:SurfaceDialTextbox.StepValue="1"
extensions:SurfaceDialTextbox.ForceMenuItem="True"
extensions:SurfaceDialTextbox.EnableHapticFeedback="True"
extensions:SurfaceDialTextbox.EnableMinMaxValue="True"
extensions:SurfaceDialTextbox.MinValue="0"
extensions:SurfaceDialTextbox.MaxValue="100"
extensions:SurfaceDialTextbox.Icon="Ruler"
extensions:SurfaceDialTextbox.EnableTapToNextControl="True"/>
总结
到这里我们就把 UWP Community Toolkit Extensions 中的 SurfaceDialTextbox 的源代码实现过程和简单的调用示例讲解完成了,希望能对大家更好的理解和使用这个扩展有所帮助。欢迎大家多多交流,谢谢!
最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。
衷心感谢 UWPCommunityToolkit 的作者们杰出的工作,Thank you so much, UWPCommunityToolkit authors!!!