diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml index 1ac842a5d..453a43379 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml @@ -4,6 +4,6 @@ xmlns:hc="https://handyorg.github.io/handycontrol" Background="{DynamicResource RegionBrush}"> - + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml.cs index 8aec171c5..03270e268 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml.cs +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/TimeBarDemoCtl.xaml.cs @@ -1,10 +1,20 @@ -namespace HandyControlDemo.UserControl +using System; +using HandyControl.Data; + +namespace HandyControlDemo.UserControl { public partial class TimeBarDemoCtl { public TimeBarDemoCtl() { InitializeComponent(); + + for (int i = 0; i < 10; i++) + { + var hour = 6 * i; + TimeBarDemo.Hotspots.Add(new DateTimeRange(DateTime.Today.AddHours(hour), DateTime.Today.AddHours(hour + 1))); + TimeBarDemo.Hotspots.Add(new DateTimeRange(DateTime.Today.AddHours(-hour), DateTime.Today.AddHours(-hour + 1))); + } } } } diff --git a/src/Shared/HandyControl_Shared/Collections/DateTimeRangeComparer.cs b/src/Shared/HandyControl_Shared/Collections/DateTimeRangeComparer.cs new file mode 100644 index 000000000..48e7f2fcc --- /dev/null +++ b/src/Shared/HandyControl_Shared/Collections/DateTimeRangeComparer.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using HandyControl.Data; + +namespace HandyControl.Collections +{ + public class DateTimeRangeComparer : IComparer + { + public int Compare(DateTimeRange x, DateTimeRange y) + { + if (x.Start > y.Start && x.End > y.End) return 1; + if (x.Start < y.Start && x.End < y.End) return -1; + return 0; + } + } +} diff --git a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs index 2d1bfd82c..7761ea819 100644 --- a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs +++ b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs @@ -131,33 +131,33 @@ public class ColorPicker : Control, ISingleOpen { new ColorRange { - Color1 = Color.FromRgb(255, 0, 0), - Color2 = Color.FromRgb(255, 0, 255) + Start = Color.FromRgb(255, 0, 0), + End = Color.FromRgb(255, 0, 255) }, new ColorRange { - Color1 = Color.FromRgb(255, 0, 255), - Color2 = Color.FromRgb(0, 0, 255) + Start = Color.FromRgb(255, 0, 255), + End = Color.FromRgb(0, 0, 255) }, new ColorRange { - Color1 = Color.FromRgb(0, 0, 255), - Color2 = Color.FromRgb(0, 255, 255) + Start = Color.FromRgb(0, 0, 255), + End = Color.FromRgb(0, 255, 255) }, new ColorRange { - Color1 = Color.FromRgb(0, 255, 255), - Color2 = Color.FromRgb(0, 255, 0) + Start = Color.FromRgb(0, 255, 255), + End = Color.FromRgb(0, 255, 0) }, new ColorRange { - Color1 = Color.FromRgb(0, 255, 0), - Color2 = Color.FromRgb(255, 255, 0) + Start = Color.FromRgb(0, 255, 0), + End = Color.FromRgb(255, 255, 0) }, new ColorRange { - Color1 = Color.FromRgb(255, 255, 0), - Color2 = Color.FromRgb(255, 0, 0) + Start = Color.FromRgb(255, 255, 0), + End = Color.FromRgb(255, 0, 0) } }; diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs index 7c3b5b5f3..287f98c60 100644 --- a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs +++ b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs @@ -77,19 +77,19 @@ public double Maximum private static void OnValueStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (TwoWayRangeBase)d; - ctrl.OnValueChanged(new TwoWayRange + ctrl.OnValueChanged(new DoubleRange { - ValueStart = (double)e.OldValue, - ValueEnd = ctrl.ValueEnd - }, new TwoWayRange + Start = (double)e.OldValue, + End = ctrl.ValueEnd + }, new DoubleRange { - ValueStart = (double)e.NewValue, - ValueEnd = ctrl.ValueEnd + Start = (double)e.NewValue, + End = ctrl.ValueEnd }); } - protected virtual void OnValueChanged(TwoWayRange oldValue, TwoWayRange newValue) => RaiseEvent( - new RoutedPropertyChangedEventArgs(oldValue, newValue) {RoutedEvent = ValueChangedEvent}); + protected virtual void OnValueChanged(DoubleRange oldValue, DoubleRange newValue) => RaiseEvent( + new RoutedPropertyChangedEventArgs(oldValue, newValue) {RoutedEvent = ValueChangedEvent}); public double ValueStart { @@ -106,14 +106,14 @@ public double ValueStart private static void OnValueEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (TwoWayRangeBase)d; - ctrl.OnValueChanged(new TwoWayRange + ctrl.OnValueChanged(new DoubleRange { - ValueStart = ctrl.ValueStart, - ValueEnd = (double)e.OldValue - }, new TwoWayRange + Start = ctrl.ValueStart, + End = (double)e.OldValue + }, new DoubleRange { - ValueStart = ctrl.ValueStart, - ValueEnd = (double)e.NewValue + Start = ctrl.ValueStart, + End = (double)e.NewValue }); } @@ -163,19 +163,12 @@ public double SmallChange } public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", - RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(TwoWayRangeBase)); + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(TwoWayRangeBase)); - public event RoutedPropertyChangedEventHandler ValueChanged + public event RoutedPropertyChangedEventHandler ValueChanged { add => AddHandler(ValueChangedEvent, value); remove => RemoveHandler(ValueChangedEvent, value); } } - - public struct TwoWayRange - { - public double ValueStart { get; set; } - - public double ValueEnd { get; set; } - } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Time/TimeBar/TimeBar.cs b/src/Shared/HandyControl_Shared/Controls/Time/TimeBar/TimeBar.cs index fd1ef960c..4c0e412a2 100644 --- a/src/Shared/HandyControl_Shared/Controls/Time/TimeBar/TimeBar.cs +++ b/src/Shared/HandyControl_Shared/Controls/Time/TimeBar/TimeBar.cs @@ -1,11 +1,17 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Shapes; using HandyControl.Data; using HandyControl.Interactivity; +using HandyControl.Tools; namespace HandyControl.Controls @@ -17,6 +23,7 @@ namespace HandyControl.Controls [TemplatePart(Name = ElementTextBlockMove, Type = typeof(TextBlock))] [TemplatePart(Name = ElementTextBlockSelected, Type = typeof(TextBlock))] [TemplatePart(Name = ElementCanvasSpe, Type = typeof(Canvas))] + [TemplatePart(Name = ElementHotspots, Type = typeof(Panel))] public class TimeBar : Control { #region Constants @@ -29,8 +36,10 @@ public class TimeBar : Control private const string ElementCanvasSpe = "PART_CanvasSpe"; + private const string ElementHotspots = "PART_Hotspots"; + #endregion Constants - + #region Data private Border _borderTop; @@ -41,14 +50,38 @@ public class TimeBar : Control private Canvas _canvasSpe; + private Panel _panelHotspots; + #endregion Data + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [Bindable(true)] + public Collection Hotspots { get; } + + public static readonly DependencyProperty HotspotsBrushProperty = DependencyProperty.Register( + "HotspotsBrush", typeof(Brush), typeof(TimeBar), new PropertyMetadata(default(Brush))); + + public Brush HotspotsBrush + { + get => (Brush) GetValue(HotspotsBrushProperty); + set => SetValue(HotspotsBrushProperty, value); + } + /// /// 是否显示刻度字符串 /// public static readonly DependencyProperty ShowSpeStrProperty = DependencyProperty.Register( "ShowSpeStr", typeof(bool), typeof(TimeBar), new PropertyMetadata(ValueBoxes.FalseBox)); + /// + /// 是否显示刻度字符串 + /// + public bool ShowSpeStr + { + get => (bool)GetValue(ShowSpeStrProperty); + set => SetValue(ShowSpeStrProperty, ValueBoxes.BooleanBox(value)); + } + public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register( "TimeFormat", typeof(string), typeof(TimeBar), new PropertyMetadata("yyyy-MM-dd HH:mm:ss")); @@ -64,6 +97,15 @@ public string TimeFormat internal static readonly DependencyProperty SpeStrProperty = DependencyProperty.Register( "SpeStr", typeof(string), typeof(TimeBar), new PropertyMetadata(Properties.Langs.Lang.Interval1h)); + /// + /// 刻度字符串 + /// + internal string SpeStr + { + get => (string)GetValue(SpeStrProperty); + set => SetValue(SpeStrProperty, value); + } + /// /// 选中时间 /// @@ -78,6 +120,15 @@ private static void OnSelectedTimeChanged(DependencyObject d, DependencyProperty } } + /// + /// 选中时间 + /// + public DateTime SelectedTime + { + get => (DateTime)GetValue(SelectedTimeProperty); + set => SetValue(SelectedTimeProperty, value); + } + private void OnSelectedTimeChanged(DateTime time) { _textBlockSelected.Text = time.ToString(TimeFormat); @@ -162,12 +213,53 @@ private void OnSelectedTimeChanged(DateTime time) private readonly bool _isLoaded; + private readonly SortedSet _dateTimeRanges; + public TimeBar() { _starTime = DateTime.Now; SelectedTime = new DateTime(_starTime.Year, _starTime.Month, _starTime.Day, 0, 0, 0); _starTime = SelectedTime; _isLoaded = true; + + var hotspots = new ObservableCollection(); + _dateTimeRanges = new SortedSet(ComparerGenerator.GetComparer()); + hotspots.CollectionChanged += Items_CollectionChanged; + Hotspots = hotspots; + } + + private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + foreach (DateTimeRange item in e.NewItems) + { + _dateTimeRanges.Add(item); + } + } + else if(e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (DateTimeRange item in e.OldItems) + { + _dateTimeRanges.Remove(item); + } + } + else if (e.Action == NotifyCollectionChangedAction.Replace) + { + foreach (DateTimeRange item in e.OldItems) + { + _dateTimeRanges.Remove(item); + } + + foreach (DateTimeRange item in e.NewItems) + { + _dateTimeRanges.Add(item); + } + } + else if (e.Action == NotifyCollectionChangedAction.Reset) + { + _dateTimeRanges.Clear(); + } } public override void OnApplyTemplate() @@ -184,6 +276,7 @@ public override void OnApplyTemplate() _textBlockMove = GetTemplateChild(ElementTextBlockMove) as TextBlock; _textBlockSelected = GetTemplateChild(ElementTextBlockSelected) as TextBlock; _canvasSpe = GetTemplateChild(ElementCanvasSpe) as Canvas; + _panelHotspots = GetTemplateChild(ElementHotspots) as Panel; CheckNull(); _borderTop.MouseLeftButtonDown += BorderTop_OnMouseLeftButtonDown; @@ -210,33 +303,6 @@ 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, ValueBoxes.BooleanBox(value)); - } - - /// - /// 刻度字符串 - /// - internal string SpeStr - { - get => (string)GetValue(SpeStrProperty); - set => SetValue(SpeStrProperty, value); - } - - /// - /// 选中时间 - /// - public DateTime SelectedTime - { - get => (DateTime)GetValue(SelectedTimeProperty); - set => SetValue(SelectedTimeProperty, value); - } - /// /// 刻度区间编号 /// @@ -323,6 +389,11 @@ private void UpdateSpeBlock() for (var i = 0; i < _speCount; i++) _speBlockList[i].Time = TimeConvert(SelectedTime).AddMilliseconds((i - sub) * _timeSpeList[_speIndex]); + + if (_panelHotspots != null && _dateTimeRanges.Any()) + { + UpdateHotspots(); + } } /// @@ -467,6 +538,18 @@ private void UpdateMouseFollowBlockPos() _textBlockMove.Margin = new Thickness(p.X - _textBlockMove.ActualWidth / 2, 2, 0, 0); } + private void UpdateHotspots() + { + var mlliseconds = ActualWidth * 0.5 / _itemWidth * _timeSpeList[_speIndex]; + if (double.IsNaN(mlliseconds) || double.IsInfinity(mlliseconds)) return; + _panelHotspots.Children.Clear(); + + foreach (var rect in GetHotspotsRectangle(mlliseconds)) + { + _panelHotspots.Children.Add(rect); + } + } + private void BorderTop_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => _borderTopIsMouseLeftButtonDown = true; private void BorderTop_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) @@ -487,5 +570,31 @@ private void BorderTop_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEven }); } } + + private IEnumerable GetHotspotsRectangle(double mlliseconds) + { + var timeSpan = TimeSpan.FromMilliseconds(mlliseconds); + var selectedTime = SelectedTime; + var start = selectedTime - timeSpan; + var end = selectedTime + timeSpan; + + var set = _dateTimeRanges.GetViewBetween(new DateTimeRange(start), new DateTimeRange(end)); + var unitLength = ActualWidth / mlliseconds * 0.5; + + foreach (var range in set) + { + var width = range.TotalMilliseconds * unitLength; + var sub = range.Start - start; + var x = sub.TotalMilliseconds * unitLength; + yield return new Rectangle + { + Fill = HotspotsBrush, + Height = 4, + Width = width, + Margin = new Thickness(x, 0, 0, 0), + HorizontalAlignment = HorizontalAlignment.Left + }; + } + } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/ColorRange.cs b/src/Shared/HandyControl_Shared/Data/ColorRange.cs deleted file mode 100644 index a5586172a..000000000 --- a/src/Shared/HandyControl_Shared/Data/ColorRange.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Windows.Media; - - -namespace HandyControl.Data -{ - /// - /// 颜色范围 - /// - public class ColorRange - { - /// - /// 颜色1 - /// - private Color _color1; - - /// - /// 颜色1 - /// - public Color Color1 - { - get => _color1; - set - { - _color1 = value; - Update(); - } - } - - /// - /// 颜色2 - /// - private Color _color2; - - /// - /// 颜色2 - /// - public Color Color2 - { - get => _color2; - set - { - _color2 = value; - Update(); - } - } - - /// - /// 颜色差值 - /// - private readonly int[] _subColorArr = new int[4]; - - /// - /// 更新 - /// - private void Update() - { - _subColorArr[0] = Color1.A - Color2.A; - _subColorArr[1] = Color1.R - Color2.R; - _subColorArr[2] = Color1.G - Color2.G; - _subColorArr[3] = Color1.B - Color2.B; - } - - /// - /// 获取指定比例处的颜色 - /// - /// 范围(0-1) - /// - public Color GetColor(double range) - { - if (range < 0 || range > 1) return default; - return Color.FromArgb((byte)(_color1.A - _subColorArr[0] * range), (byte)(_color1.R - _subColorArr[1] * range), - (byte)(_color1.G - _subColorArr[2] * range), (byte)(_color1.B - _subColorArr[3] * range)); - } - } -} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/EnumDataProvider.cs b/src/Shared/HandyControl_Shared/Data/Enum/EnumDataProvider.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Data/EnumDataProvider.cs rename to src/Shared/HandyControl_Shared/Data/Enum/EnumDataProvider.cs diff --git a/src/Shared/HandyControl_Shared/Data/GrowlInfo.cs b/src/Shared/HandyControl_Shared/Data/Info/GrowlInfo.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Data/GrowlInfo.cs rename to src/Shared/HandyControl_Shared/Data/Info/GrowlInfo.cs diff --git a/src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs b/src/Shared/HandyControl_Shared/Data/Info/ImageCodecInfo.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Data/ImageCodecInfo.cs rename to src/Shared/HandyControl_Shared/Data/Info/ImageCodecInfo.cs diff --git a/src/Shared/HandyControl_Shared/Data/MessageBoxInfo.cs b/src/Shared/HandyControl_Shared/Data/Info/MessageBoxInfo.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Data/MessageBoxInfo.cs rename to src/Shared/HandyControl_Shared/Data/Info/MessageBoxInfo.cs diff --git a/src/Shared/HandyControl_Shared/Data/SystemVersionInfo.cs b/src/Shared/HandyControl_Shared/Data/Info/SystemVersionInfo.cs similarity index 98% rename from src/Shared/HandyControl_Shared/Data/SystemVersionInfo.cs rename to src/Shared/HandyControl_Shared/Data/Info/SystemVersionInfo.cs index 6ea2a5370..0da9c89db 100644 --- a/src/Shared/HandyControl_Shared/Data/SystemVersionInfo.cs +++ b/src/Shared/HandyControl_Shared/Data/Info/SystemVersionInfo.cs @@ -5,7 +5,7 @@ namespace HandyControl.Data { - public struct SystemVersionInfo + public readonly struct SystemVersionInfo { public static SystemVersionInfo Windows10 => new SystemVersionInfo(10, 0, 10240); diff --git a/src/Shared/HandyControl_Shared/Data/Range/ColorRange.cs b/src/Shared/HandyControl_Shared/Data/Range/ColorRange.cs new file mode 100644 index 000000000..7d946925e --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Range/ColorRange.cs @@ -0,0 +1,63 @@ +using System.Windows.Media; + + +namespace HandyControl.Data +{ + /// + /// 颜色范围 + /// + public class ColorRange : IValueRange + { + private Color _start; + + public Color Start + { + get => _start; + set + { + _start = value; + Update(); + } + } + + private Color _end; + + public Color End + { + get => _end; + set + { + _end = value; + Update(); + } + } + + /// + /// 颜色差值 + /// + private readonly int[] _subColorArr = new int[4]; + + /// + /// 更新 + /// + private void Update() + { + _subColorArr[0] = _start.A - _end.A; + _subColorArr[1] = _start.R - _end.R; + _subColorArr[2] = _start.G - _end.G; + _subColorArr[3] = _start.B - _end.B; + } + + /// + /// 获取指定比例处的颜色 + /// + /// 范围(0-1) + /// + public Color GetColor(double range) + { + if (range < 0 || range > 1) return default; + return Color.FromArgb((byte)(_start.A - _subColorArr[0] * range), (byte)(_start.R - _subColorArr[1] * range), + (byte)(_start.G - _subColorArr[2] * range), (byte)(_start.B - _subColorArr[3] * range)); + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/Range/DateTimeRange.cs b/src/Shared/HandyControl_Shared/Data/Range/DateTimeRange.cs new file mode 100644 index 000000000..bd5225acf --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Range/DateTimeRange.cs @@ -0,0 +1,25 @@ +using System; + +namespace HandyControl.Data +{ + public struct DateTimeRange : IValueRange + { + public DateTimeRange(DateTime start) + { + Start = start; + End = start; + } + + public DateTimeRange(DateTime start, DateTime end) + { + Start = start; + End = end; + } + + public DateTime Start { get; set; } + + public DateTime End { get; set; } + + public double TotalMilliseconds => (End - Start).TotalMilliseconds; + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/Range/DoubleRange.cs b/src/Shared/HandyControl_Shared/Data/Range/DoubleRange.cs new file mode 100644 index 000000000..592c17712 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Range/DoubleRange.cs @@ -0,0 +1,9 @@ +namespace HandyControl.Data +{ + public struct DoubleRange : IValueRange + { + public double Start { get; set; } + + public double End { get; set; } + } +} diff --git a/src/Shared/HandyControl_Shared/Data/Range/IValueRange.cs b/src/Shared/HandyControl_Shared/Data/Range/IValueRange.cs new file mode 100644 index 000000000..644a2eb5e --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Range/IValueRange.cs @@ -0,0 +1,9 @@ +namespace HandyControl.Data +{ + public interface IValueRange + { + T Start { get; set; } + + T End { get; set; } + } +} diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index a2dc57024..43a2d51c7 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -72,8 +72,10 @@ + + - + @@ -88,7 +90,9 @@ - + + + @@ -126,6 +130,7 @@ + @@ -223,7 +228,7 @@ - + @@ -243,14 +248,14 @@ - + - + - + @@ -1084,6 +1089,7 @@ + \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/TimeBar.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/TimeBar.xaml index 9a60f3996..c067a8045 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/TimeBar.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/TimeBar.xaml @@ -9,6 +9,7 @@