Custom LED Control in WPF

在现代应用程序中,用户界面的直观性和响应性是至关重要的。为了增强用户体验,开发者经常需要创建具有动态视觉效果的自定义控件。本文将介绍如何在WPF应用程序中创建一个自定义的LED控制,它能够以动画形式显示不同组件的状态。这个LED控制不仅能够以代码形式动态创建,而且还可以配置,无需使用图像或额外资源。

首先,考虑了一个交通信号灯的模型,它默认显示三个LED(绿色、橙色、红色),分别代表组件的状态(正常、警告、错误)。目标是创建一个动画效果,使得只有一个LED在任何时候处于激活状态。为了实现这一点,设计了一个LED控制,它具备创建和动画功能。

为了遵循良好的编程实践,创建了两个程序集。一个模拟通用基础设施库,包含"LedControl"类;另一个程序集则在WPF应用程序中展示这个控件。虽然在实际项目中,通常会使用Prism框架来处理复合模式、依赖注入、MVVM等,但为了保持示例的简单性,在这个例子中直接在MainWindow的XAML代码中分配了ViewModel。

LED控制具有一些可配置的依赖属性:

  • Leds(颜色列表):用于显示定义颜色的LED。默认颜色为绿色、橙色和红色。
  • OffOpacity(透明度):LED颜色在关闭模式下的透明度,默认值为0.4。
  • ActiveLed(激活LED):激活的LED索引。例如,0表示没有激活,1表示列表中的第一个LED激活,依此类推。
  • LedOrientation(LED方向):LED的方向,水平或垂直,默认为水平。

LED的方向非常重要,因为它决定了当方向为水平时需要定义自定义控件的高度,而当方向为垂直时需要定义宽度。如果没有定义,自定义控件将使用可用的大小。

控件使用StackPanel,其中包含一系列Ellipse(LED)控件。Ellipse的画刷用于显示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()和LedOff()来运行动画(在ActiveLed改变后),通过改变ellipses画刷的透明度。检查ActiveLed和当前Opacity是为了确保不会同时打开和关闭同一个LED。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485