using JinYuan.ControlLib.UCHelper; using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace JinYuan.ControlLib { //public partial class UCRollText : UserControl //{ // /// // /// The m intdirection // /// // int m_intdirection = 1; // /// // /// The rect text // /// // Rectangle rectText; // /// // /// The m timer // /// // System.Timers.Timer m_timer; // /// // /// The roll style // /// // private RollStyle _RollStyle = RollStyle.LeftToRight; // /// // /// 文本改变是否重新从边缘运动 // /// // private bool _ISChangeReset = false; // private readonly SemaphoreSlim _updateLock = new SemaphoreSlim(1, 1); // private CancellationTokenSource _cts = new CancellationTokenSource(); // private volatile bool _isStop = true; // private Rectangle _rectText; // private int _moveDirection = 1; // private const int DEFAULT_MOVE_STEP = 5; // private const int DEFAULT_SLEEP_TIME = 200; // #region Properties // public override Font Font // { // get => base.Font; // set // { // base.Font = value; // UpdateTextRectangle(); // } // } // public override string Text // { // get => base.Text; // set // { // base.Text = value; // UpdateTextRectangle(); // } // } // private RollStyle _rollStyle = RollStyle.LeftToRight; // public RollStyle RollStyle // { // get => _rollStyle; // set // { // _rollStyle = value; // _moveDirection = value == RollStyle.RightToLeft ? -1 : 1; // ResetTextPosition(); // } // } // private bool _isChangeReset; // [Description("文本改变是否重新从边缘运动"), Category("自定义")] // public bool IsChangeReset // { // get => _isChangeReset; // set => _isChangeReset = value; // } // private bool _isExit; // [Description("是否退出"), Category("自定义")] // public bool IsExit // { // get => _isExit; // set // { // _isExit = value; // if (value) // { // StopAnimation(); // } // } // } // private int _moveSleepTime = DEFAULT_SLEEP_TIME; // [Description("每次滚动间隔时间,越小速度越快"), Category("自定义")] // public int MoveSleepTime // { // get => _moveSleepTime; // set => _moveSleepTime = value > 0 ? value : DEFAULT_SLEEP_TIME; // } // #endregion // public UCRollText() // { // InitializeControl(); // InitializeEvents(); // StartAnimation(); // } // private void InitializeControl() // { // SetStyle(ControlStyles.AllPaintingInWmPaint | // ControlStyles.DoubleBuffer | // ControlStyles.ResizeRedraw | // ControlStyles.Selectable | // ControlStyles.SupportsTransparentBackColor | // ControlStyles.UserPaint, true); // Size = new Size(450, 30); // Text = "滚动文字"; // ForeColor = Color.FromArgb(255, 77, 59); // } // private void InitializeEvents() // { // Load += UCRollText_Load; // SizeChanged += UCRollText_SizeChanged; // VisibleChanged += (s, e) => // { // _isStop = !Visible; // if (Visible) // { // StartAnimation(); // } // }; // } // private void UCRollText_SizeChanged(object sender, EventArgs e) // { // if (rectText != Rectangle.Empty) // { // rectText.Y = (this.Height - rectText.Height) / 2 + 1; // } // } // private void UCRollText_Load(object sender, EventArgs e) // { // if (_RollStyle == RollStyle.LeftToRight) // { // m_intdirection = 1; // if (rectText != Rectangle.Empty) // { // rectText.X = -1 * rectText.Width - 1; // } // } // else if (_RollStyle == RollStyle.RightToLeft) // { // m_intdirection = -1; // if (rectText != Rectangle.Empty) // { // rectText.X = this.Width + rectText.Width + 1; // } // } // else // { // m_intdirection = 1; // if (rectText != Rectangle.Empty) // { // rectText.X = 0; // } // } // if (rectText != Rectangle.Empty) // { // rectText.Y = (this.Height - rectText.Height) / 2 + 1; // } // } // private async void StartAnimation() // { // if (!_isStop) return; // _isStop = false; // _cts = new CancellationTokenSource(); // try // { // await Task.Run(async () => // { // while (!_isStop && !_isExit) // { // try // { // await Task.Delay(MoveSleepTime, _cts.Token); // if (!IsHandleCreated || IsDisposed) continue; // await UpdateTextPositionAsync(); // } // catch (OperationCanceledException) // { // break; // } // catch (Exception ex) // { // Debug.WriteLine($"Animation error: {ex.Message}"); // } // } // }, _cts.Token); // } // catch (OperationCanceledException) { } // } // private void StopAnimation() // { // _isStop = true; // try // { // _cts?.Cancel(); // _cts?.Dispose(); // _cts = null; // } // catch { } // } // private async Task UpdateTextPositionAsync() // { // if (_rectText == Rectangle.Empty) return; // if (_rollStyle == RollStyle.BackAndForth && _rectText.Width >= Width) return; // await _updateLock.WaitAsync(); // try // { // // 使用 Invoke 方式 // if (InvokeRequired) // { // Invoke((MethodInvoker)delegate // { // UpdateTextPosition(); // }); // } // else // { // UpdateTextPosition(); // } // } // finally // { // _updateLock.Release(); // } // } // private void UpdateTextPosition() // { // _rectText.X += DEFAULT_MOVE_STEP * _moveDirection; // switch (_rollStyle) // { // case RollStyle.BackAndForth: // if (_rectText.X <= 0) // _moveDirection = 1; // else if (_rectText.Right >= Width) // _moveDirection = -1; // break; // case RollStyle.LeftToRight: // if (_rectText.X > Width) // _rectText.X = -1 * _rectText.Width - 1; // break; // case RollStyle.RightToLeft: // if (_rectText.Right < 0) // _rectText.X = Width + _rectText.Width + 1; // break; // } // Invalidate(true); // } // private void UpdateTextRectangle() // { // if (string.IsNullOrEmpty(Text)) return; // using (var g = CreateGraphics()) // { // var size = g.MeasureString(Text, Font); // _rectText = new Rectangle( // _isChangeReset ? 0 : _rectText.X, // (Height - (int)size.Height) / 2 + 1, // (int)size.Width, // (int)size.Height // ); // } // } // private void ResetTextPosition() // { // if (_rectText == Rectangle.Empty) return; // switch (_rollStyle) // { // case RollStyle.LeftToRight: // _rectText.X = -1 * _rectText.Width - 1; // break; // case RollStyle.RightToLeft: // _rectText.X = Width + _rectText.Width + 1; // break; // default: // _rectText.X = 0; // break; // } // } // protected override void OnPaint(PaintEventArgs e) // { // base.OnPaint(e); // if (_rectText != Rectangle.Empty) // { // e.Graphics.SetGDIHigh(); // using (var brush = new SolidBrush(ForeColor)) // { // e.Graphics.DrawString(Text, Font, brush, _rectText.Location); // } // } // } //} public partial class UCRollText : UserControl { private Rectangle _rectText; private int _moveDirection = 1; private const int DEFAULT_MOVE_STEP = 2; // 减小步长,使滚动更平滑 private const int DEFAULT_SLEEP_TIME = 20; // 减小刷新间隔,提高流畅度 private readonly SemaphoreSlim _updateLock = new SemaphoreSlim(1, 1); private CancellationTokenSource _cts; private volatile bool _isStop = true; #region Properties public override Font Font { get => base.Font; set { base.Font = value; UpdateTextRectangle(); } } public override string Text { get => base.Text; set { base.Text = value; UpdateTextRectangle(); } } private RollStyle _rollStyle = RollStyle.LeftToRight; public RollStyle RollStyle { get => _rollStyle; set { _rollStyle = value; _moveDirection = value == RollStyle.RightToLeft ? -1 : 1; ResetTextPosition(); } } private bool _isChangeReset; [Description("文本改变是否重新从边缘运动"), Category("自定义")] public bool IsChangeReset { get => _isChangeReset; set => _isChangeReset = value; } private bool _isExit; [Description("是否退出"), Category("自定义")] public bool IsExit { get => _isExit; set { _isExit = value; if (value) StopAnimation(); } } private int _moveSleepTime = DEFAULT_SLEEP_TIME; [Description("每次滚动间隔时间,越小速度越快"), Category("自定义")] public int MoveSleepTime { get => _moveSleepTime; set => _moveSleepTime = value > 0 ? value : DEFAULT_SLEEP_TIME; } #endregion public UCRollText() { InitializeControl(); InitializeEvents(); StartAnimation(); } private void InitializeControl() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.Selectable | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true); DoubleBuffered = true; // 启用双缓冲,减少闪烁 Size = new Size(450, 30); Text = "滚动文字"; ForeColor = Color.FromArgb(255, 77, 59); } private void InitializeEvents() { Load += UCRollText_Load; SizeChanged += UCRollText_SizeChanged; VisibleChanged += (s, e) => { _isStop = !Visible; if (Visible) StartAnimation(); }; } private void UCRollText_SizeChanged(object sender, EventArgs e) { if (_rectText != Rectangle.Empty) { _rectText.Y = (Height - _rectText.Height) / 2 + 1; } } private void UCRollText_Load(object sender, EventArgs e) { ResetTextPosition(); } private async void StartAnimation() { if (!_isStop) return; _isStop = false; _cts = new CancellationTokenSource(); try { await Task.Run(async () => { var stopwatch = new Stopwatch(); stopwatch.Start(); while (!_isStop && !_isExit) { await Task.Delay(MoveSleepTime, _cts.Token); if (!IsHandleCreated || IsDisposed) continue; try { var elapsed = stopwatch.ElapsedMilliseconds; stopwatch.Restart(); await UpdateTextPositionAsync(elapsed); } catch (OperationCanceledException) { break; } catch (Exception ex) { Debug.WriteLine($"Animation error: {ex.Message}"); } } }, _cts.Token); } catch (OperationCanceledException) { } } private void StopAnimation() { _isStop = true; _cts?.Cancel(); _cts?.Dispose(); _cts = null; } private async Task UpdateTextPositionAsync(long elapsedMilliseconds) { if (_rectText == Rectangle.Empty) return; if (_rollStyle == RollStyle.BackAndForth && _rectText.Width >= Width) return; await _updateLock.WaitAsync(); try { //await Task.Run(() => //{ if (InvokeRequired) { BeginInvoke((MethodInvoker)(() => UpdateTextPosition(elapsedMilliseconds))); } else { UpdateTextPosition(elapsedMilliseconds); } //}); } finally { _updateLock.Release(); } } private void UpdateTextPosition(long elapsedMilliseconds) { // 根据时间调整滚动步长,确保动画平滑 int step = (int)(DEFAULT_MOVE_STEP * (elapsedMilliseconds / (double)MoveSleepTime)); _rectText.X += step * _moveDirection; switch (_rollStyle) { case RollStyle.BackAndForth: if (_rectText.X <= 0) _moveDirection = 1; else if (_rectText.Right >= Width) _moveDirection = -1; break; case RollStyle.LeftToRight: if (_rectText.X > Width) _rectText.X = -_rectText.Width - 1; break; case RollStyle.RightToLeft: if (_rectText.Right < 0) _rectText.X = Width + _rectText.Width + 1; break; } Invalidate(); } private void UpdateTextRectangle() { if (string.IsNullOrEmpty(Text)) return; using (var g = CreateGraphics()) { var size = g.MeasureString(Text, Font); _rectText = new Rectangle( _isChangeReset ? 0 : _rectText.X, (Height - (int)size.Height) / 2 + 1, (int)size.Width, (int)size.Height ); } } private void ResetTextPosition() { if (_rectText == Rectangle.Empty) return; switch (_rollStyle) { case RollStyle.LeftToRight: _rectText.X = -_rectText.Width - 1; break; case RollStyle.RightToLeft: _rectText.X = Width + _rectText.Width + 1; break; default: _rectText.X = 0; break; } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (_rectText != Rectangle.Empty) { e.Graphics.SetGDIHigh(); using (var brush = new SolidBrush(ForeColor)) { e.Graphics.DrawString(Text, Font, brush, _rectText.Location); } } } } public enum RollStyle { LeftToRight, RightToLeft, BackAndForth } }