diff --git a/AlarmScrollingText.cs b/AlarmScrollingText.cs
new file mode 100644
index 0000000..5a32845
--- /dev/null
+++ b/AlarmScrollingText.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Threading;
+
+namespace PLCDataMonitor
+{
+ ///
+ /// 自定义滚动报警控件(跑马灯效果 + 闪烁)
+ ///
+ public class AlarmScrollingText : ContentControl
+ {
+ private TextBlock _textBlock;
+ private Border _clipBorder;
+ private Storyboard _scrollStoryboard;
+ private Storyboard _blinkStoryboard;
+ private double _currentOffset = 0;
+ private DispatcherTimer _resetTimer;
+
+ public static readonly DependencyProperty TextProperty =
+ DependencyProperty.Register("Text", typeof(string), typeof(AlarmScrollingText),
+ new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var control = d as AlarmScrollingText;
+ string newText = e.NewValue as string ?? string.Empty;
+ control?.OnTextChanged(newText);
+ }
+
+ public AlarmScrollingText()
+ {
+ // 默认样式
+ DefaultStyleKey = typeof(AlarmScrollingText);
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ _textBlock = GetTemplateChild("PART_TextBlock") as TextBlock;
+ _clipBorder = GetTemplateChild("PART_ClipBorder") as Border;
+
+ if (_textBlock != null && _clipBorder != null)
+ {
+ _textBlock.Loaded += OnTextBlockLoaded;
+ _textBlock.SizeChanged += OnTextBlockSizeChanged;
+ InitializeAnimations();
+ }
+ }
+
+ private void OnTextBlockLoaded(object sender, RoutedEventArgs e)
+ {
+ StartScrollingIfNeeded();
+ }
+
+ private void OnTextBlockSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ StartScrollingIfNeeded();
+ }
+
+ private void OnTextChanged(string newText)
+ {
+ if (_textBlock != null)
+ {
+ _textBlock.Text = newText;
+ StartScrollingIfNeeded();
+ StartBlinking(newText);
+ }
+ }
+
+ private void InitializeAnimations()
+ {
+ // 滚动动画
+ _scrollStoryboard = new Storyboard();
+ var doubleAnimation = new DoubleAnimation
+ {
+ From = 0,
+ To = -500, // 临时值,后面动态计算
+ Duration = TimeSpan.FromSeconds(8),
+ RepeatBehavior = RepeatBehavior.Forever
+ };
+ Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
+ _scrollStoryboard.Children.Add(doubleAnimation);
+
+ // 闪烁动画(前景色交替变化)
+ _blinkStoryboard = new Storyboard { RepeatBehavior = RepeatBehavior.Forever };
+ var colorAnimation = new ColorAnimation
+ {
+ From = Colors.Red,
+ To = Colors.Orange,
+ Duration = TimeSpan.FromSeconds(0.5),
+ AutoReverse = true,
+ RepeatBehavior = RepeatBehavior.Forever
+ };
+ Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("Foreground.Color"));
+ _blinkStoryboard.Children.Add(colorAnimation);
+ }
+
+ private void StartScrollingIfNeeded()
+ {
+ if (_textBlock == null || _clipBorder == null) return;
+
+ // 停止当前动画
+ _scrollStoryboard?.Stop(_textBlock);
+ _textBlock.RenderTransform = new TranslateTransform();
+
+ // 如果没有报警或报警文本为空,不启动滚动
+ if (string.IsNullOrWhiteSpace(Text) || Text == "无报警")
+ return;
+
+ // 等待布局完成后获取实际宽度
+ Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
+ {
+ double textWidth = _textBlock.ActualWidth;
+ double containerWidth = _clipBorder.ActualWidth;
+
+ if (textWidth <= containerWidth)
+ {
+ // 文本不够长,不需要滚动,但保持静止
+ return;
+ }
+
+ // 重新设置动画的目标偏移量
+ double targetOffset = containerWidth - textWidth;
+ var animation = _scrollStoryboard.Children[0] as DoubleAnimation;
+ if (animation != null)
+ {
+ animation.From = 0;
+ animation.To = targetOffset;
+ animation.Duration = TimeSpan.FromSeconds(Math.Max(4, textWidth / 50.0)); // 速度自适应
+ }
+
+ _textBlock.RenderTransform = new TranslateTransform();
+ _scrollStoryboard.Begin(_textBlock, true);
+ }));
+ }
+
+ private void StartBlinking(string alarmText)
+ {
+ if (_textBlock == null) return;
+
+ // 停止当前闪烁
+ _blinkStoryboard?.Stop(_textBlock);
+
+ // 如果是无效报警或无报警,恢复正常颜色并停止闪烁
+ if (string.IsNullOrWhiteSpace(alarmText) || alarmText == "无报警")
+ {
+ _textBlock.Foreground = SystemColors.ControlTextBrush;
+ return;
+ }
+
+ // 开始闪烁动画
+ _blinkStoryboard.Begin(_textBlock, true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/App.config b/App.config
index 09fbc3f..1375969 100644
--- a/App.config
+++ b/App.config
@@ -1,8 +1,8 @@
-
-
+
+
diff --git a/App.xaml b/App.xaml
index 9695b8d..90b02dc 100644
--- a/App.xaml
+++ b/App.xaml
@@ -1,9 +1,13 @@
-
-
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
index 1ebebcf..9f34ac9 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -2,7 +2,7 @@
using System.Data;
using System.Windows;
-namespace 滑动摩擦测试仪
+namespace PLCDataMonitor
{
///
/// Interaction logic for App.xaml
diff --git a/HomePage.xaml b/HomePage.xaml
index 094788c..be86264 100644
--- a/HomePage.xaml
+++ b/HomePage.xaml
@@ -31,12 +31,12 @@
-
+
+
diff --git a/HomePage.xaml.cs b/HomePage.xaml.cs
index 717a5c3..d85675c 100644
--- a/HomePage.xaml.cs
+++ b/HomePage.xaml.cs
@@ -31,7 +31,9 @@ namespace PLCDataMonitor
public void UpdateAlarms(string alarmMessage)
{
- Dispatcher.Invoke(() => AlarmTextBlock.Text = alarmMessage);
+
+ Dispatcher.Invoke(() => // 更新滚动报警控件的文本
+ AlarmScrollingCtrl.Text = alarmMessage);
}
// 更新数据显示
diff --git a/Themes/Generic.xaml b/Themes/Generic.xaml
new file mode 100644
index 0000000..4073925
--- /dev/null
+++ b/Themes/Generic.xaml
@@ -0,0 +1,31 @@
+
+
+
+
\ No newline at end of file
diff --git a/滑动摩擦测试仪.csproj b/滑动摩擦测试仪.csproj
index 3980986..badea60 100644
--- a/滑动摩擦测试仪.csproj
+++ b/滑动摩擦测试仪.csproj
@@ -20,4 +20,10 @@
+
+
+ Always
+
+
+