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; 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); } } }