178 lines
6.1 KiB
C#
178 lines
6.1 KiB
C#
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;
|
|
|
|
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);
|
|
|
|
// 添加一个简单的默认样式,以防找不到模板
|
|
this.Loaded += OnLoaded;
|
|
}
|
|
|
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
// 确保获取到模板中的元素
|
|
ApplyTemplate();
|
|
|
|
_textBlock = GetTemplateChild("PART_TextBlock") as TextBlock;
|
|
_clipBorder = GetTemplateChild("PART_ClipBorder") as Border;
|
|
|
|
if (_textBlock != null && _clipBorder != null)
|
|
{
|
|
_textBlock.SizeChanged += (s, args) => CheckAndStartScrolling();
|
|
_clipBorder.SizeChanged += (s, args) => CheckAndStartScrolling();
|
|
|
|
// 初始化滚动动画
|
|
InitializeScrollAnimation();
|
|
|
|
// 检查是否需要滚动
|
|
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(CheckAndStartScrolling));
|
|
}
|
|
else
|
|
{
|
|
// 如果找不到模板元素,添加一个简单的 TextBlock 作为后备
|
|
CreateFallbackUI();
|
|
}
|
|
}
|
|
|
|
private void CreateFallbackUI()
|
|
{
|
|
var border = new Border
|
|
{
|
|
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));
|
|
}
|
|
|
|
private void InitializeScrollAnimation()
|
|
{
|
|
if (_textBlock == null) return;
|
|
|
|
_scrollStoryboard = new Storyboard();
|
|
var doubleAnimation = new DoubleAnimation
|
|
{
|
|
From = 0,
|
|
To = -500, // 初始值,稍后在 CheckAndStartScrolling 中动态更新
|
|
Duration = TimeSpan.FromSeconds(5),
|
|
RepeatBehavior = RepeatBehavior.Forever
|
|
};
|
|
Storyboard.SetTarget(doubleAnimation, _textBlock);
|
|
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
|
|
_scrollStoryboard.Children.Add(doubleAnimation);
|
|
}
|
|
|
|
|
|
private void OnTextChanged(string newText)
|
|
{
|
|
if (_textBlock != null)
|
|
{
|
|
_textBlock.Text = newText;
|
|
// 延迟一下,等待布局更新后再检查
|
|
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(CheckAndStartScrolling));
|
|
}
|
|
}
|
|
|
|
private void CheckAndStartScrolling()
|
|
{
|
|
if (_textBlock == null || _clipBorder == null || _scrollStoryboard == null)
|
|
return;
|
|
|
|
// 停止当前动画
|
|
_scrollStoryboard.Stop(_textBlock);
|
|
_textBlock.RenderTransform = null;
|
|
|
|
// 如果没有报警或报警文本为"无报警",不启动滚动
|
|
if (string.IsNullOrWhiteSpace(Text) || Text == "无报警")
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 强制更新布局以获取准确宽度
|
|
_textBlock.UpdateLayout();
|
|
_clipBorder.UpdateLayout();
|
|
|
|
double textWidth = _textBlock.ActualWidth;
|
|
double containerWidth = _clipBorder.ActualWidth;
|
|
|
|
// 如果文本宽度小于等于容器宽度,不需要滚动
|
|
if (textWidth <= containerWidth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 计算滚动目标位置
|
|
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);
|
|
}
|
|
|
|
}
|
|
} |