Files
2026-09-09 18:42:38 +08:00

621 lines
18 KiB
C#

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
//{
// /// <summary>
// /// The m intdirection
// /// </summary>
// int m_intdirection = 1;
// /// <summary>
// /// The rect text
// /// </summary>
// Rectangle rectText;
// /// <summary>
// /// The m timer
// /// </summary>
// System.Timers.Timer m_timer;
// /// <summary>
// /// The roll style
// /// </summary>
// private RollStyle _RollStyle = RollStyle.LeftToRight;
// /// <summary>
// /// 文本改变是否重新从边缘运动
// /// </summary>
// 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
}
}