WPF LED自定义控件(后台代码实现)
代码背后实现的LED(例如,交通灯)控件。可自定义颜色、透明度和尺寸。
引言
我们需要一个用于WPF窗口基础架构库的控件,以可视方式显示应用程序中不同组件的状态。我脑海中冒出的最明显的东西是一个交通信号灯,默认情况下,它向我显示三个LED(绿色、橙色、红色),指示组件的状态(正常、警告、错误),其中只有一个LED可以处于活动状态。
为了让最终用户满意,我们希望使它们具有动画效果。 为了我自己,我想使它们具有动画效果、可配置、不使用图像且不使用资源。因此,我创建了一个LED控件,其中包含在代码背后实现的控件创建和动画功能。
Using the Code
为了良好的实践,我创建了两个程序集。 一个程序集模拟包含“LedControl
”的公共基础架构库,另一个程序集在WPF应用程序中显示该控件。 我通常使用Prism进行组合模式、注入、MVVM等,但为了使此示例简单,我在MainWindow
程序集的MVVM文件夹中创建了两个类,并在MainWindow
XAML代码中直接分配了ViewModel
。 这不是一个好习惯,但我想保持示例简单。 LED控件是在考虑特定合同的情况下创建的,因此仅在该合同中进行了测试。
控件属性
该控件具有以下可配置的依赖项属性
List<Color> Leds
用于以定义的颜色显示LED的颜色列表。 默认为绿色、橙色和红色。
下面的图像显示了默认LED的开/关模式。
绿色 | 橙色 | 红色 |
![]() | ![]() | ![]() |
double OffOpacity
LED颜色在关闭模式下的不透明度。 默认为0.4。
int ActiveLed
LED的活动索引。 例如,0=没有活动,1=LED列表中第一个LED处于活动状态,2=第二个,等等。
Orientation LedOrientation
LED的方向。 水平或垂直。 默认为水平。
如果不是,请设置自定义控件将使用可用的大小。
该控件使用一个StackPanel
,其中包含一个Ellipse(LED)控件列表。 椭圆的笔刷用于显示LED的颜色并模拟开/关行为。
LED的加载发生在控件加载后,以便我们知道框架元素的大小。 加载事件在构造函数中注册
public LedControl()
{
Loaded += LoadLeds;
}
控件的创建发生在LoadLeds
方法中,该方法在控件加载后或Leds
属性更改后调用。
private void LoadLeds(object sender, RoutedEventArgs e)
{
FrameworkElement parent = Parent as FrameworkElement;
StackPanel panel = new StackPanel();
Content = panel;
panel.Orientation = LedOrientation;
panel.Children.Clear();
ellipses.Clear();
double size;
if (LedOrientation == Orientation.Horizontal)
{
size = Height;
}
else
{
size = Width;
}
// Give it some size if forgotten to define width or height in combination with orientation
if ((size.Equals(double.NaN)) && (parent != null) && (Leds.Count != 0))
{
if (parent.ActualWidth != double.NaN)
{
size = parent.ActualWidth / Leds.Count;
}
else if (parent.ActualHeight != double.NaN)
{
size = parent.ActualHeight / Leds.Count;
}
}
// Create LED for each defined color in Leds
foreach (Color color in Leds)
{
Ellipse ellipse = new Ellipse();
ellipse.Height = size > 4 ? size - 4 : size;
ellipse.Width = size > 4 ? size - 4 : size;
ellipse.Margin = new Thickness(2);
ellipse.Style = null;
// Border for led
RadialGradientBrush srgb = new RadialGradientBrush(new GradientStopCollection
{
new GradientStop(Color.FromArgb(255, 211, 211, 211), 0.8d),
new GradientStop(Color.FromArgb(255, 169, 169, 169), 0.9d),
new GradientStop(Color.FromArgb(255, 150, 150, 150), 0.95d),
});
if (size <= 50)
{
ellipse.StrokeThickness = 5;
}
else if (size <= 100)
{
ellipse.StrokeThickness = 10;
}
else
{
ellipse.StrokeThickness = 20;
}
srgb.GradientOrigin = new System.Windows.Point(0.5d, 0.5d);
srgb.Center = new System.Windows.Point(0.5d, 0.5d);
srgb.RadiusX = 0.5d;
srgb.RadiusY = 0.5d;
ellipse.Stroke = srgb;
// Color of led
RadialGradientBrush rgb = new RadialGradientBrush(new GradientStopCollection
{
new GradientStop(Color.FromArgb(150, color.R, color.G, color.B), 0.1d),
new GradientStop(Color.FromArgb(200, color.R, color.G, color.B), 0.4d),
new GradientStop(Color.FromArgb(255, color.R, color.G, color.B), 1.0d),
});
rgb.GradientOrigin = new System.Windows.Point(0.5d, 0.5d);
rgb.Center = new System.Windows.Point(0.5d, 0.5d);
rgb.RadiusX = 0.5d;
rgb.RadiusY = 0.5d;
// ellipse.Fill is used as animation target
ellipse.Fill = rgb;
ellipse.Fill.Opacity = OffOpacity;
panel.Children.Add(ellipse);
ellipses.Add(ellipse);
}
LedOn();
}
两种方法 LedOn()
private void LedOn()
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = OffOpacity;
animation.To = 1.0d;
animation.Duration = new Duration(TimeSpan.FromSeconds(1));
animation.AutoReverse = false;
for (int i = 0; i < ellipses.Count; i++)
{
if ((ActiveLed - 1 == i) && (ellipses[i].Fill.Opacity < 1.0))
{
ellipses[i].Fill.BeginAnimation(Brush.OpacityProperty, animation);
}
}
}
和 LedOff()
private void LedOff()
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = 1.0d;
animation.To = OffOpacity;
animation.Duration = new Duration(TimeSpan.FromSeconds(1));
animation.AutoReverse = false;
for (int i = 0; i < ellipses.Count; i++)
{
if ((ActiveLed - 1 != i) && (ellipses[i].Fill.Opacity > OffOpacity))
{
ellipses[i].Fill.BeginAnimation(Brush.OpacityProperty, animation);
}
}
}
通过更改椭圆笔刷上的不透明度,在ActiveLed
更改后运行动画。 检查ActiveLed
和当前Opacity
是为了确保我们不会同时打开和关闭同一个LED。
如何使用控件
在MainWindowxaml
中,您可以找到一些有关如何使用控件和测试您自己创建的内容的示例。 一些如下所示
<controls:LedControl ActiveLed="{Binding ActiveLedItem1}"
LedOrientation="Vertical" Width="50" />
<controls:LedControl ActiveLed="{Binding ActiveLedItem1}"
Height="125" />
<controls:LedControl ActiveLed="{Binding ActiveLedItem1}"
LedOrientation="Vertical" Width="50" Leds="{Binding Colors}" />
历史
- 2018年8月22日:首次发布