This commit is contained in:
164
AlarmScrollingText.cs
Normal file
164
AlarmScrollingText.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义滚动报警控件(跑马灯效果 + 闪烁)
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user