前言
软件开发中,UI组件的可定制性和视觉表现力越来越受到重视。WPF(Windows Presentation Foundation)作为一款强大的界面开发框架,提供了丰富的样式与模板机制,可以轻松创建自定义控件。
本文将带你一步步实现一个自定义的单数码管控件,通过继承 Control 类并结合 XAML 样式与后台逻辑代码,完成一个支持数字显示、外观定制、数据绑定等功能的数码管控件。
该控件适用于仪表盘、计数器、工业控制等需要数字展示的场景,具有良好的扩展性与复用价值。
项目目标
创建一个基于 WPF 的自定义控件:SingleDigitalTube
支持显示数字 0~9,超出范围显示错误标识 "E"
可自定义背景色、边框颜色、边框粗细
支持数据绑定,便于集成到 MVVM 架构中
使用 Path 路径绘制七段数码管结构,保证缩放不失真
绘制数码管图形
使用矢量绘图工具(如 Inkscape)绘制一个标准的七段数码管图形,并导出为 SVG 或直接提取其 Path 数据用于 WPF 控件。
以下是七段数码管的 Path 定义:
<Path x:Name="tube1" Data="M 1.1889374,0.49999738 51.351967,51.757227 H 150.79113 L 202.04941,0.49999738 Z"/>
<Path x:Name="tube2" Data="m 203.23948,25.604947 -51.25723,50.16303 v 99.439163 l 51.25723,51.25828 z"/>
<Path x:Name="tube3" Data="m 202.43163,275.21452 -51.25723,50.16303 v 99.43916 l 51.25723,51.25828 z"/>
<Path x:Name="tube4" Data="M 202.06759,501.59746 151.90456,450.34023 H 52.465396 L 1.2071159,501.59746 Z"/>
<Path x:Name="tube5" Data="M 1.9025759,476.49251 53.159806,426.32948 V 326.89032 L 1.9025759,275.63204 Z"/>
<Path x:Name="tube6" Data="M 2.7104259,226.88294 53.967656,176.71991 V 77.280747 L 2.7104259,26.022467 Z"/>
<Path x:Name="tube7" Data="M 53.140896,201.36022 1.8826159,251.42644 53.140896,300.89664 H 152.0813 l 51.8543,-49.4702 -51.8543,-50.06622 z"/>
这些 Path 元素分别代表数码管的七段亮起部分,我们将在 ControlTemplate 中引用它们,并根据当前数值动态设置其填充颜色。
代码实现
1、创建控件类:SingleDigitalTube.cs
该类继承自 Control,定义了一个 Value 属性,并监听其变化以更新数码管显示状态。
public class SingleDigitalTube : Control
{
private Path? tuble1, tuble2, tuble3, tuble4, tuble5, tuble6, tuble7;
private Brush TransparentBrush { get; } = new SolidColorBrush(Colors.Transparent);
public int Value
{
get => (int)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(SingleDigitalTube),
new PropertyMetadata(0, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SingleDigitalTube control && control.IsLoaded)
{
control.SetDigitalTube((int)e.NewValue);
}
}
private void SetDigitalTube(int num)
{
if (num >= 0 && num <= 9)
{
tuble1!.Fill = (num != 1 && num != 4) ? Background : TransparentBrush;
tuble2!.Fill = (num != 5 && num != 6) ? Background : TransparentBrush;
tuble3!.Fill = num != 2 ? Background : TransparentBrush;
tuble4!.Fill = (num != 1 && num != 4 && num != 7) ? Background : TransparentBrush;
tuble5!.Fill = (new[] { 0, 2, 6, 8 }.Contains(num)) ? Background : TransparentBrush;
tuble6!.Fill = !(new[] { 1, 2, 3, 7 }.Contains(num)) ? Background : TransparentBrush;
tuble7!.Fill = (num != 0 && num != 1 && num != 7) ? Background : TransparentBrush;
}
else
{
// 显示 E
tuble1!.Fill = Background;
tuble2!.Fill = Background;
tuble3!.Fill = TransparentBrush;
tuble4!.Fill = Background;
tuble5!.Fill = Background;
tuble6!.Fill = Background;
tuble7!.Fill = Background;
}
}
private void SetStrokeThickness()
{
var thickness = (BorderThickness.Left + BorderThickness.Right + BorderThickness.Top + BorderThickness.Bottom) / 4;
tuble1!.StrokeThickness = thickness;
tuble2!.StrokeThickness = thickness;
tuble3!.StrokeThickness = thickness;
tuble4!.StrokeThickness = thickness;
tuble5!.StrokeThickness = thickness;
tuble6!.StrokeThickness = thickness;
tuble7!.StrokeThickness = thickness;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
tuble1 = GetTemplateChild("tube1") as Path;
tuble2 = GetTemplateChild("tube2") as Path;
tuble3 = GetTemplateChild("tube3") as Path;
tuble4 = GetTemplateChild("tube4") as Path;
tuble5 = GetTemplateChild("tube5") as Path;
tuble6 = GetTemplateChild("tube6") as Path;
tuble7 = GetTemplateChild("tube7") as Path;
SetStrokeThickness();
SetDigitalTube(Value);
}
static SingleDigitalTube()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SingleDigitalTube),
new FrameworkPropertyMetadata(typeof(SingleDigitalTube)));
}
}
2、定义控件样式:Generic.xaml
在 Themes/Generic.xaml 中定义控件的默认样式和模板:
<Style TargetType="{x:Type local:SingleDigitalTube}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Viewbox Stretch="Uniform">
<Grid>
<Path x:Name="tube1" ... />
<Path x:Name="tube2" ... />
<Path x:Name="tube3" ... />
<Path x:Name="tube4" ... />
<Path x:Name="tube5" ... />
<Path x:Name="tube6" ... />
<Path x:Name="tube7" ... />
</Grid>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
使用示例
在 XAML 页面中引入命名空间后即可使用:
<local:SingleDigitalTube
Height="50"
Background="Red"
BorderBrush="Black"
BorderThickness="5"
Value="{Binding ElementName=setNumber, Path=Value}" />
其中 setNumber 是一个滑块或输入控件,用于动态修改数码管显示值。
运行效果
控件会根据传入的 Value 值实时刷新七段数码管的状态,支持从 0 到 9 的数字显示,超出范围则显示 “E”。
总结
本文详细讲解了如何使用 WPF 实现一个自定义的单数码管控件,包括:
-
使用 Path 绘制矢量图形
-
自定义控件继承
Control并添加依赖属性 -
在模板中绑定路径元素
-
动态控制各段点亮状态
-
支持多种外观定制和数据绑定
该控件不仅可用于实际项目中的数字展示,也可以作为学习 WPF 自定义控件开发的一个良好示例。后续你可以进一步拓展功能,如支持小数点、多数码管联动、动画效果等,以满足更复杂的应用需求。
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:奇点旅行者
出处:mp.weixin.qq.com/s/2tk7jfD2-06sF-RzzAaEXQ
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!