using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using HandyControl.Data; using HandyControl.Interactivity; using HandyControl.Tools; namespace HandyControl.Controls { /// /// 时间条 /// [TemplatePart(Name = ElementBorderTop, Type = typeof(Border))] [TemplatePart(Name = ElementTextBlockMove, Type = typeof(TextBlock))] [TemplatePart(Name = ElementTextBlockSelected, Type = typeof(TextBlock))] [TemplatePart(Name = ElementCanvasSpe, Type = typeof(Canvas))] public class TimeBar : Control { #region Constants private const string ElementBorderTop = "PART_BorderTop"; private const string ElementTextBlockMove = "PART_TextBlockMove"; private const string ElementTextBlockSelected = "PART_TextBlockSelected"; private const string ElementCanvasSpe = "PART_CanvasSpe"; #endregion Constants #region Data private Border _borderTop; private TextBlock _textBlockMove; private TextBlock _textBlockSelected; private Canvas _canvasSpe; #endregion Data /// /// 是否显示刻度字符串 /// public static readonly DependencyProperty ShowSpeStrProperty = DependencyProperty.Register( "ShowSpeStr", typeof(bool), typeof(TimeBar), new PropertyMetadata(ValueBoxes.FalseBox)); public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register( "TimeFormat", typeof(string), typeof(TimeBar), new PropertyMetadata("yyyy-MM-dd HH:mm:ss")); public string TimeFormat { get => (string) GetValue(TimeFormatProperty); set => SetValue(TimeFormatProperty, value); } /// /// 刻度字符串 /// internal static readonly DependencyProperty SpeStrProperty = DependencyProperty.Register( "SpeStr", typeof(string), typeof(TimeBar), new PropertyMetadata(Properties.Langs.Lang.Interval1h)); /// /// 选中时间 /// internal static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register( "SelectedTime", typeof(DateTime), typeof(TimeBar), new PropertyMetadata(default(DateTime), OnSelectedTimeChanged)); private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TimeBar timeBar && timeBar._textBlockSelected != null) { timeBar._textBlockSelected.Text = ((DateTime) e.NewValue).ToString(timeBar.TimeFormat); } } /// /// 时间改变事件 /// public static readonly RoutedEvent TimeChangedEvent = EventManager.RegisterRoutedEvent("TimeChanged", RoutingStrategy.Bubble, typeof(EventHandler>), typeof(TimeBar)); /// /// 刻度集合 /// private readonly List _speBlockList = new List(); /// /// 初始化时时间 /// private readonly DateTime _starTime; /// /// 时间段集合 /// private readonly List _timeSpeList = new List { 7200000, 3600000, 1800000, 600000, 300000, 60000, 30000 }; /// /// 顶部border是否被按下 /// private bool _borderTopIsMouseLeftButtonDown; /// /// 控件是否处于拖动中 /// private bool _isDragging; /// /// 刻度单项宽度 /// private double _itemWidth; /// /// 鼠标按下拖动时选中的时间 /// private DateTime _mouseDownTime; /// /// 显示的刻度数目 /// private int _speCount = 13; /// /// 刻度区间编号 /// private int _speIndex = 1; /// /// 刻度单次偏移 /// private double _tempOffsetX; /// /// 刻度总偏移 /// private double _totalOffsetX; private readonly bool _isLoaded; public TimeBar() { _starTime = DateTime.Now; SelectedTime = new DateTime(_starTime.Year, _starTime.Month, _starTime.Day, 0, 0, 0); _starTime = SelectedTime; _isLoaded = true; SizeChanged += TimeBar_OnSizeChanged; MouseMove += TimeBar_OnMouseMove; } public override void OnApplyTemplate() { if (_borderTop != null) { _borderTop.MouseLeftButtonDown -= BorderTop_OnMouseLeftButtonDown; _borderTop.PreviewMouseLeftButtonUp -= BorderTop_OnPreviewMouseLeftButtonUp; } base.OnApplyTemplate(); _borderTop = GetTemplateChild(ElementBorderTop) as Border; _textBlockMove = GetTemplateChild(ElementTextBlockMove) as TextBlock; _textBlockSelected = GetTemplateChild(ElementTextBlockSelected) as TextBlock; _canvasSpe = GetTemplateChild(ElementCanvasSpe) as Canvas; CheckNull(); _borderTop.MouseLeftButtonDown += BorderTop_OnMouseLeftButtonDown; _borderTop.PreviewMouseLeftButtonUp += BorderTop_OnPreviewMouseLeftButtonUp; var behavior = new MouseDragElementBehaviorEx { LockY = true, }; behavior.DragBegun += DragElementBehavior_OnDragBegun; behavior.Dragging += MouseDragElementBehavior_OnDragging; behavior.DragFinished += MouseDragElementBehavior_OnDragFinished; var collection = Interaction.GetBehaviors(_borderTop); collection.Add(behavior); if (_isLoaded) { TimeBar_OnSizeChanged(null, null); } _textBlockSelected.Text = SelectedTime.ToString(TimeFormat); } private void CheckNull() { if (_borderTop == null || _textBlockMove == null|| _textBlockSelected == null || _canvasSpe == null) throw new Exception(); } /// /// 是否显示刻度字符串 /// public bool ShowSpeStr { get => (bool)GetValue(ShowSpeStrProperty); set => SetValue(ShowSpeStrProperty, value); } /// /// 刻度字符串 /// internal string SpeStr { get => (string)GetValue(SpeStrProperty); set => SetValue(SpeStrProperty, value); } /// /// 选中时间 /// internal DateTime SelectedTime { get => (DateTime)GetValue(SelectedTimeProperty); set => SetValue(SelectedTimeProperty, value); } /// /// 刻度区间编号 /// public int SpeIndex { get => _speIndex; private set { if (_speIndex == value) return; if (value < 0) { SpeStr = Properties.Langs.Lang.Interval2h; _speIndex = 0; return; } if (value > 6) { SpeStr = Properties.Langs.Lang.Interval30s; _speIndex = 6; return; } SetSpeTimeFormat("HH:mm"); switch (value) { case 0: SpeStr = Properties.Langs.Lang.Interval2h; break; case 1: SpeStr = Properties.Langs.Lang.Interval1h; break; case 2: SpeStr = Properties.Langs.Lang.Interval30m; break; case 3: SpeStr = Properties.Langs.Lang.Interval10m; break; case 4: SpeStr = Properties.Langs.Lang.Interval5m; break; case 5: SpeStr = Properties.Langs.Lang.Interval1m; break; case 6: SetSpeTimeFormat("HH:mm:ss"); SpeStr = Properties.Langs.Lang.Interval30s; break; } _speIndex = value; } } /// /// 时间改变事件 /// public event EventHandler> TimeChanged { add => AddHandler(TimeChangedEvent, value); remove => RemoveHandler(TimeChangedEvent, value); } /// /// 设置刻度时间格式 /// /// private void SetSpeTimeFormat(string format) { foreach (var item in _speBlockList) item.TimeFormat = format; } /// /// 设置显示的时间 /// /// public void SetShowTime(DateTime time) { if (_isDragging || _borderTopIsMouseLeftButtonDown) return; SelectedTime = time; _totalOffsetX = (_starTime - SelectedTime).TotalMilliseconds / _timeSpeList[SpeIndex] * _itemWidth; Update(); TimeBar_OnMouseMove(null, null); } /// /// 更新 /// private void Update() { var rest = (_totalOffsetX + _tempOffsetX) % _itemWidth; for (var i = 0; i < _speCount; i++) { var item = _speBlockList[i]; item.MoveX(rest + (_itemWidth - item.Width) / 2); } var sub = rest <= 0 ? _speCount / 2 : _speCount / 2 - 1; for (var i = 0; i < _speCount; i++) _speBlockList[i].Time = TimeConvert(SelectedTime).AddMilliseconds((i - sub) * _timeSpeList[_speIndex]); } /// /// 时间转换 /// /// /// private DateTime TimeConvert(DateTime time) { switch (_speIndex) { case 0: return new DateTime(time.Year, time.Month, time.Day, time.Hour / 2 * 2, 0, 0); case 1: return new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0); case 2: return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 30 * 30, 0); case 3: return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 10 * 10, 0); case 4: return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 5 * 5, 0); case 5: return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, 0); case 6: return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second / 30 * 30); default: return time; } } /// /// 鼠标滚轮滚动时改变刻度区间 /// /// protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); if (Mouse.LeftButton == MouseButtonState.Pressed) return; SpeIndex += e.Delta > 0 ? 1 : -1; _totalOffsetX = (_starTime - SelectedTime).TotalMilliseconds / _timeSpeList[SpeIndex] * _itemWidth; Update(); TimeBar_OnMouseMove(null, null); e.Handled = true; } private void MouseDragElementBehavior_OnDragging(object sender, MouseEventArgs e) { _isDragging = true; _tempOffsetX = _borderTop.RenderTransform.Value.OffsetX; SelectedTime = _mouseDownTime - TimeSpan.FromMilliseconds(_tempOffsetX / _itemWidth * _timeSpeList[_speIndex]); Update(); _borderTopIsMouseLeftButtonDown = false; } private void MouseDragElementBehavior_OnDragFinished(object sender, MouseEventArgs e) { _tempOffsetX = 0; _totalOffsetX = (_totalOffsetX + _borderTop.RenderTransform.Value.OffsetX) % ActualWidth; _borderTop.RenderTransform = new TranslateTransform(); RaiseEvent(new FunctionEventArgs(TimeChangedEvent, this) { Info = SelectedTime }); _isDragging = false; } private void DragElementBehavior_OnDragBegun(object sender, MouseEventArgs e) => _mouseDownTime = SelectedTime; private void TimeBar_OnSizeChanged(object sender, SizeChangedEventArgs e) { _speBlockList.Clear(); _canvasSpe.Children.Clear(); _speCount = (int)(ActualWidth / 800 * 9) | 1; var itemWidthOld = _itemWidth; _itemWidth = ActualWidth / _speCount; _totalOffsetX = _itemWidth / itemWidthOld * _totalOffsetX % ActualWidth; if (double.IsNaN(_totalOffsetX)) _totalOffsetX = 0; var rest = (_totalOffsetX + _tempOffsetX) % _itemWidth; var sub = rest <= 0 || double.IsNaN(rest) ? _speCount / 2 : _speCount / 2 - 1; for (var i = 0; i < _speCount; i++) { var block = new SpeTextBlock { Time = TimeConvert(SelectedTime).AddMilliseconds((i - sub) * _timeSpeList[_speIndex]), TextAlignment = TextAlignment.Center, TimeFormat = "HH:mm" }; _speBlockList.Add(block); _canvasSpe.Children.Add(block); } if (_speIndex == 6) SetSpeTimeFormat("HH:mm:ss"); ShowSpeStr = ActualWidth > 320; for (var i = 0; i < _speCount; i++) { var item = _speBlockList[i]; item.X = _itemWidth * i; item.MoveX((_itemWidth - item.Width) / 2); } Update(); TimeBar_OnMouseMove(null, null); } private void TimeBar_OnMouseMove(object sender, MouseEventArgs e) { var p = Mouse.GetPosition(this); var mlliseconds = (p.X - ActualWidth / 2) / _itemWidth * _timeSpeList[_speIndex]; if (double.IsInfinity(mlliseconds)) return; _textBlockMove.Text = mlliseconds < 0 ? (SelectedTime - TimeSpan.FromMilliseconds(-mlliseconds)).ToString(TimeFormat) : (SelectedTime + TimeSpan.FromMilliseconds(mlliseconds)).ToString(TimeFormat); _textBlockMove.Margin = new Thickness(p.X - _textBlockMove.ActualWidth / 2, 2, 0, 0); } private void BorderTop_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => _borderTopIsMouseLeftButtonDown = true; private void BorderTop_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (_borderTopIsMouseLeftButtonDown) { _borderTopIsMouseLeftButtonDown = false; var p = Mouse.GetPosition(this); _tempOffsetX = ActualWidth / 2 - p.X; SelectedTime -= TimeSpan.FromMilliseconds(_tempOffsetX / _itemWidth * _timeSpeList[_speIndex]); Update(); _totalOffsetX = (_totalOffsetX + _tempOffsetX) % ActualWidth; _tempOffsetX = 0; TimeBar_OnMouseMove(null, null); RaiseEvent(new FunctionEventArgs(TimeChangedEvent, this) { Info = SelectedTime }); } } } }