Files
huadongmocaceshiyi/AlarmScrollingText.cs

178 lines
6.1 KiB
C#
Raw Normal View History

2026-03-23 17:31:25 +08:00
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>
2026-03-24 19:34:22 +08:00
/// 滚动报警控件(跑马灯效果)
2026-03-23 17:31:25 +08:00
/// </summary>
public class AlarmScrollingText : ContentControl
{
private TextBlock _textBlock;
private Border _clipBorder;
private Storyboard _scrollStoryboard;
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);
2026-03-24 19:34:22 +08:00
// 添加一个简单的默认样式,以防找不到模板
this.Loaded += OnLoaded;
2026-03-23 17:31:25 +08:00
}
2026-03-24 19:34:22 +08:00
private void OnLoaded(object sender, RoutedEventArgs e)
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
// 确保获取到模板中的元素
ApplyTemplate();
2026-03-23 17:31:25 +08:00
_textBlock = GetTemplateChild("PART_TextBlock") as TextBlock;
_clipBorder = GetTemplateChild("PART_ClipBorder") as Border;
if (_textBlock != null && _clipBorder != null)
{
2026-03-24 19:34:22 +08:00
_textBlock.SizeChanged += (s, args) => CheckAndStartScrolling();
_clipBorder.SizeChanged += (s, args) => CheckAndStartScrolling();
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
// 初始化滚动动画
InitializeScrollAnimation();
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
// 检查是否需要滚动
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(CheckAndStartScrolling));
}
else
{
// 如果找不到模板元素,添加一个简单的 TextBlock 作为后备
CreateFallbackUI();
}
2026-03-23 17:31:25 +08:00
}
2026-03-24 19:34:22 +08:00
private void CreateFallbackUI()
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
var border = new Border
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
Background = this.Background,
CornerRadius = new CornerRadius(4),
ClipToBounds = true
};
_textBlock = new TextBlock
{
Text = this.Text,
FontSize = 14,
FontWeight = FontWeights.Bold,
Foreground = Brushes.Red,
TextWrapping = TextWrapping.NoWrap,
VerticalAlignment = VerticalAlignment.Center
};
border.Child = _textBlock;
this.Content = border;
_clipBorder = border;
_textBlock.SizeChanged += (s, args) => CheckAndStartScrolling();
_clipBorder.SizeChanged += (s, args) => CheckAndStartScrolling();
InitializeScrollAnimation();
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(CheckAndStartScrolling));
2026-03-23 17:31:25 +08:00
}
2026-03-24 19:34:22 +08:00
private void InitializeScrollAnimation()
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
if (_textBlock == null) return;
2026-03-23 17:31:25 +08:00
_scrollStoryboard = new Storyboard();
var doubleAnimation = new DoubleAnimation
{
From = 0,
2026-03-24 19:34:22 +08:00
To = -500, // 初始值,稍后在 CheckAndStartScrolling 中动态更新
Duration = TimeSpan.FromSeconds(5),
2026-03-23 17:31:25 +08:00
RepeatBehavior = RepeatBehavior.Forever
};
2026-03-24 19:34:22 +08:00
Storyboard.SetTarget(doubleAnimation, _textBlock);
2026-03-23 17:31:25 +08:00
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
_scrollStoryboard.Children.Add(doubleAnimation);
2026-03-24 19:34:22 +08:00
}
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
private void OnTextChanged(string newText)
{
if (_textBlock != null)
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
_textBlock.Text = newText;
// 延迟一下,等待布局更新后再检查
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(CheckAndStartScrolling));
}
2026-03-23 17:31:25 +08:00
}
2026-03-24 19:34:22 +08:00
private void CheckAndStartScrolling()
2026-03-23 17:31:25 +08:00
{
2026-03-24 19:34:22 +08:00
if (_textBlock == null || _clipBorder == null || _scrollStoryboard == null)
return;
2026-03-23 17:31:25 +08:00
// 停止当前动画
2026-03-24 19:34:22 +08:00
_scrollStoryboard.Stop(_textBlock);
_textBlock.RenderTransform = null;
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
// 如果没有报警或报警文本为"无报警",不启动滚动
2026-03-23 17:31:25 +08:00
if (string.IsNullOrWhiteSpace(Text) || Text == "无报警")
{
2026-03-24 19:34:22 +08:00
return;
}
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
// 强制更新布局以获取准确宽度
_textBlock.UpdateLayout();
_clipBorder.UpdateLayout();
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
double textWidth = _textBlock.ActualWidth;
double containerWidth = _clipBorder.ActualWidth;
2026-03-23 17:31:25 +08:00
2026-03-24 19:34:22 +08:00
// 如果文本宽度小于等于容器宽度,不需要滚动
if (textWidth <= containerWidth)
2026-03-23 17:31:25 +08:00
{
return;
}
2026-03-24 19:34:22 +08:00
// 计算滚动目标位置
double targetOffset = -textWidth; // 滚动到文本的左边界
// 更新动画参数
var animation = _scrollStoryboard.Children[0] as DoubleAnimation;
if (animation != null)
{
animation.From = containerWidth; // 从容器右边开始
animation.To = targetOffset; // 滚动到文本的左边界
// 根据文本长度计算滚动速度
double speed = Math.Max(3, Math.Abs(textWidth + containerWidth) / 50.0);
animation.Duration = TimeSpan.FromSeconds(speed);
}
// 设置初始变换
_textBlock.RenderTransform = new TranslateTransform();
// 开始滚动动画
_scrollStoryboard.Begin(_textBlock, true);
2026-03-23 17:31:25 +08:00
}
2026-03-24 19:34:22 +08:00
2026-03-23 17:31:25 +08:00
}
}