Adjust project structure

This commit is contained in:
NaBian committed 2019-04-05 22:23:19 +08:00
1 parent d38912f5da
commit e3c436f939
2111 files changed
+14028 -47199

No files matched your search

@@ -0,0 +1,260 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
[TemplatePart(Name = ElementClockPresenter, Type = typeof(ContentPresenter))]
[TemplatePart(Name = ElementCalendarPresenter, Type = typeof(ContentPresenter))]
public class CalendarWithClock : Control
{
#region Constants
private const string ElementButtonConfirm = "PART_ButtonConfirm";
private const string ElementClockPresenter = "PART_ClockPresenter";
private const string ElementCalendarPresenter = "PART_CalendarPresenter";
#endregion Constants
#region Data
private ContentPresenter _clockPresenter;
private ContentPresenter _calendarPresenter;
private Clock _clock;
private Calendar _calendar;
private Button _buttonConfirm;
private bool _isLoaded;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedDateTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(CalendarWithClock));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
{
add => AddHandler(SelectedDateTimeChangedEvent, value);
remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<DateTime>> DisplayDateTimeChanged;
public event Action Confirmed;
#endregion Public Events
public CalendarWithClock()
{
InitCalendarAndClock();
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
DisplayDateTime = SelectedDateTime ?? DateTime.Now;
};
}
#region Public Properties
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(CalendarWithClock), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string DateTimeFormat
{
get => (string)GetValue(DateTimeFormatProperty);
set => SetValue(DateTimeFormatProperty, value);
}
public static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
"ShowConfirmButton", typeof(bool), typeof(CalendarWithClock), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowConfirmButton
{
get => (bool)GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, value);
}
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(CalendarWithClock), new PropertyMetadata(default(DateTime?), OnSelectedDateTimeChanged));
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock)d;
var v = (DateTime?)e.NewValue;
ctl.OnSelectedDateTimeChanged(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, ctl)
{
Info = v
});
}
public DateTime? SelectedDateTime
{
get => (DateTime?)GetValue(SelectedDateTimeProperty);
set => SetValue(SelectedDateTimeProperty, value);
}
public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
"DisplayDateTime", typeof(DateTime), typeof(CalendarWithClock), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateTimeChanged));
private static void OnDisplayDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock)d;
if (ctl.IsHandlerSuspended(DisplayDateTimeProperty)) return;
var v = (DateTime)e.NewValue;
ctl._clock.SelectedTime = v;
ctl._calendar.SelectedDate = v;
ctl.OnDisplayDateTimeChanged(new FunctionEventArgs<DateTime>(v));
}
public DateTime DisplayDateTime
{
get => (DateTime)GetValue(DisplayDateTimeProperty);
set => SetValue(DisplayDateTimeProperty, value);
}
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
if (_buttonConfirm != null)
{
_buttonConfirm.Click -= ButtonConfirm_OnClick;
}
base.OnApplyTemplate();
_buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
_clockPresenter = GetTemplateChild(ElementClockPresenter) as ContentPresenter;
_calendarPresenter = GetTemplateChild(ElementCalendarPresenter) as ContentPresenter;
CheckNull();
_clockPresenter.Content = _clock;
_calendarPresenter.Content = _calendar;
_buttonConfirm.Click += ButtonConfirm_OnClick;
}
#endregion
#region Protected Methods
protected virtual void OnSelectedDateTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
protected virtual void OnDisplayDateTimeChanged(FunctionEventArgs<DateTime> e)
{
var handler = DisplayDateTimeChanged;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void CheckNull()
{
if (_buttonConfirm == null || _clockPresenter == null || _calendarPresenter == null) throw new Exception();
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
{
SelectedDateTime = DisplayDateTime;
Confirmed?.Invoke();
}
private void InitCalendarAndClock()
{
_clock = new Clock
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent
};
TitleElement.SetBackground(_clock, Brushes.Transparent);
_clock.DisplayTimeChanged += Clock_DisplayTimeChanged;
_calendar = new Calendar
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent,
Focusable = false
};
TitleElement.SetBackground(_calendar, Brushes.Transparent);
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
}
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
Mouse.Capture(null);
UpdateDisplayTime();
}
private void Clock_DisplayTimeChanged(object sender, FunctionEventArgs<DateTime> e) => UpdateDisplayTime();
private void UpdateDisplayTime()
{
if (_calendar.SelectedDate != null)
{
var date = _calendar.SelectedDate.Value;
var time = _clock.DisplayTime;
var result = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
SetValueNoCallback(DisplayDateTimeProperty, result);
}
}
#endregion
}
}
@@ -0,0 +1,431 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementButtonAm, Type = typeof(RadioButton))]
[TemplatePart(Name = ElementButtonPm, Type = typeof(RadioButton))]
[TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
[TemplatePart(Name = ElementCanvas, Type = typeof(Canvas))]
[TemplatePart(Name = ElementBorderTitle, Type = typeof(Border))]
[TemplatePart(Name = ElementBorderClock, Type = typeof(Border))]
[TemplatePart(Name = ElementPanelNum, Type = typeof(CirclePanel))]
[TemplatePart(Name = ElementTimeStr, Type = typeof(TextBlock))]
public class Clock : Control
{
#region Constants
private const string ElementButtonAm = "PART_ButtonAm";
private const string ElementButtonPm = "PART_ButtonPm";
private const string ElementButtonConfirm = "PART_ButtonConfirm";
private const string ElementCanvas = "PART_Canvas";
private const string ElementBorderTitle = "PART_BorderTitle";
private const string ElementBorderClock = "PART_BorderClock";
private const string ElementPanelNum = "PART_PanelNum";
private const string ElementTimeStr = "PART_TimeStr";
#endregion Constants
#region Data
private RadioButton _buttonAm;
private RadioButton _buttonPm;
private Button _buttonConfirm;
private Canvas _canvas;
private Border _borderTitle;
private Border _borderClock;
private ClockRadioButton _currentButton;
private RotateTransform _rotateTransformClock;
private CirclePanel _circlePanel;
private List<ClockRadioButton> _radioButtonList;
private TextBlock _blockTime;
private int _secValue;
private bool _appliedTemplate;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(Clock));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedTimeChanged
{
add => AddHandler(SelectedTimeChangedEvent, value);
remove => RemoveHandler(SelectedTimeChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<DateTime>> DisplayTimeChanged;
public event Action Confirmed;
#endregion Public Events
#region Public Properties
public static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
"SelectedTime", typeof(DateTime?), typeof(Clock), new PropertyMetadata(default(DateTime?), OnSelectedTimeChanged));
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Clock)d;
var v = (DateTime?)e.NewValue;
ctl.DisplayTime = v ?? DateTime.Now;
ctl.OnSelectedTimeChanged(new FunctionEventArgs<DateTime?>(SelectedTimeChangedEvent, ctl)
{
Info = v
});
}
public DateTime? SelectedTime
{
get => (DateTime?) GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register(
"TimeFormat", typeof(string), typeof(Clock), new PropertyMetadata("HH:mm:ss"));
public string TimeFormat
{
get => (string) GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
public static readonly DependencyProperty ClockRadioButtonStyleProperty = DependencyProperty.Register(
"ClockRadioButtonStyle", typeof(Style), typeof(Clock), new PropertyMetadata(default(Style)));
public Style ClockRadioButtonStyle
{
get => (Style) GetValue(ClockRadioButtonStyleProperty);
set => SetValue(ClockRadioButtonStyleProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty = DependencyProperty.Register(
"DisplayTime", typeof(DateTime), typeof(Clock), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayTimeChanged));
private static void OnDisplayTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Clock) d;
var v = (DateTime) e.NewValue;
ctl.Update(v);
ctl.OnDisplayTimeChanged(new FunctionEventArgs<DateTime>(v));
}
public DateTime DisplayTime
{
get => (DateTime) GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
internal static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
"ShowConfirmButton", typeof(bool), typeof(Clock), new PropertyMetadata(ValueBoxes.FalseBox));
internal bool ShowConfirmButton
{
get => (bool)GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, value);
}
private int SecValue
{
get => _secValue;
set
{
if (value < 0)
{
_secValue = 59;
}
else if (value > 59)
{
_secValue = 0;
}
else
{
_secValue = value;
}
}
}
#endregion Public Properties
#region Public Methods
public override void OnApplyTemplate()
{
_appliedTemplate = false;
if (_buttonAm != null)
{
_buttonAm.Click -= ButtonAm_OnClick;
}
if (_buttonPm != null)
{
_buttonPm.Click -= ButtonPm_OnClick;
}
if (_buttonConfirm != null)
{
_buttonConfirm.Click -= ButtonConfirm_OnClick;
}
if (_borderTitle != null)
{
_borderTitle.MouseWheel -= BorderTitle_OnMouseWheel;
}
if (_canvas != null)
{
_canvas.MouseWheel -= Canvas_OnMouseWheel;
_canvas.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Canvas_OnClick));
_canvas.MouseMove -= Canvas_OnMouseMove;
}
base.OnApplyTemplate();
_buttonAm = GetTemplateChild(ElementButtonAm) as RadioButton;
_buttonPm = GetTemplateChild(ElementButtonPm) as RadioButton;
_buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
_borderTitle = GetTemplateChild(ElementBorderTitle) as Border;
_canvas = GetTemplateChild(ElementCanvas) as Canvas;
_borderClock = GetTemplateChild(ElementBorderClock) as Border;
_circlePanel = GetTemplateChild(ElementPanelNum) as CirclePanel;
_blockTime = GetTemplateChild(ElementTimeStr) as TextBlock;
CheckNull();
_buttonAm.Click += ButtonAm_OnClick;
_buttonPm.Click += ButtonPm_OnClick;
_buttonConfirm.Click += ButtonConfirm_OnClick;
_borderTitle.MouseWheel += BorderTitle_OnMouseWheel;
_canvas.MouseWheel += Canvas_OnMouseWheel;
_canvas.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Canvas_OnClick));
_canvas.MouseMove += Canvas_OnMouseMove;
_rotateTransformClock = new RotateTransform();
_borderClock.RenderTransform = _rotateTransformClock;
_radioButtonList = new List<ClockRadioButton>();
for (var i = 0; i < 12; i++)
{
var num = i + 1;
var button = new ClockRadioButton
{
Num = num,
Content = num
};
button.SetBinding(StyleProperty, new Binding(ClockRadioButtonStyleProperty.Name) { Source = this });
_radioButtonList.Add(button);
_circlePanel.Children.Add(button);
}
_appliedTemplate = true;
if (SelectedTime.HasValue)
{
Update(SelectedTime.Value);
}
else
{
DisplayTime = DateTime.Now;
Update(DisplayTime);
}
}
#endregion Public Methods
#region Protected Methods
protected virtual void OnSelectedTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
protected virtual void OnDisplayTimeChanged(FunctionEventArgs<DateTime> e)
{
var handler = DisplayTimeChanged;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_buttonPm == null || _buttonAm == null || _buttonConfirm == null || _canvas == null ||
_borderTitle == null || _borderClock == null || _circlePanel == null ||
_blockTime == null) throw new Exception();
}
private void BorderTitle_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta < 0)
{
SecValue--;
Update();
}
else
{
SecValue++;
Update();
}
e.Handled = true;
}
private void Canvas_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var value = (int)_rotateTransformClock.Angle;
if (e.Delta < 0)
{
value += 6;
}
else
{
value -= 6;
}
if (value < 0)
{
value = value + 360;
}
_rotateTransformClock.Angle = value;
Update();
e.Handled = true;
}
private void Canvas_OnClick(object sender, RoutedEventArgs e)
{
_currentButton = e.OriginalSource as ClockRadioButton;
if (_currentButton != null)
{
Update();
}
}
private void Canvas_OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var value = ArithmeticHelper.CalAngle(new Point(85, 85), e.GetPosition(_canvas)) + 90;
if (value < 0)
{
value = value + 360;
}
value = value - value % 6;
_rotateTransformClock.Angle = value;
Update();
}
}
private void Update()
{
if (!_appliedTemplate) return;
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
if (hValue == 12 && _buttonAm.IsChecked == true)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
if (_blockTime != null)
{
DisplayTime = GetDisplayTime();
_blockTime.Text = DisplayTime.ToString(TimeFormat);
}
}
/// <summary>
/// 更新
/// </summary>
/// <param name="time"></param>
internal void Update(DateTime time)
{
if (!_appliedTemplate) return;
var h = time.Hour;
var m = time.Minute;
if (h >= 12)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
else
{
_buttonPm.IsChecked = false;
_buttonAm.IsChecked = true;
}
_rotateTransformClock.Angle = m * 6;
var hRest = h % 12;
if (hRest == 0) hRest = 12;
var ctl = _radioButtonList[hRest-1];
ctl.IsChecked = true;
ctl.RaiseEvent(new RoutedEventArgs { RoutedEvent = ButtonBase.ClickEvent });
_secValue = time.Second;
Update();
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
{
SelectedTime = DisplayTime;
Confirmed?.Invoke();
}
/// <summary>
/// 获取显示时间
/// </summary>
/// <returns></returns>
private DateTime GetDisplayTime()
{
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
var now = DateTime.Now;
return new DateTime(now.Year, now.Month, now.Day, hValue, (int)Math.Abs(_rotateTransformClock.Angle) % 360 / 6, _secValue);
}
private void ButtonAm_OnClick(object sender, RoutedEventArgs e) => Update();
private void ButtonPm_OnClick(object sender, RoutedEventArgs e) => Update();
#endregion Private Methods
}
}
@@ -0,0 +1,10 @@
using System.Windows.Controls;
namespace HandyControl.Controls
{
public class ClockRadioButton : RadioButton
{
public int Num { get; set; }
}
}
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace HandyControl.Controls
{
public class FlipClock : Control
{
public static readonly DependencyProperty NumberListProperty = DependencyProperty.Register(
"NumberList", typeof(List<int>), typeof(FlipClock), new PropertyMetadata(new List<int> {0, 0, 0, 0, 0, 0}));
public List<int> NumberList
{
get => (List<int>) GetValue(NumberListProperty);
set => SetValue(NumberListProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty = DependencyProperty.Register(
"DisplayTime", typeof(DateTime), typeof(FlipClock), new PropertyMetadata(default(DateTime), OnDisplayTimeChanged));
private static void OnDisplayTimeChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctl = (FlipClock) s;
var v = (DateTime) e.NewValue;
ctl.NumberList = new List<int>
{
v.Hour / 10,
v.Hour % 10,
v.Minute / 10,
v.Minute % 10,
v.Second / 10,
v.Second % 10
};
}
public DateTime DisplayTime
{
get => (DateTime) GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
public FlipClock()
{
var dispatcherTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(200)
};
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e) => DisplayTime = DateTime.Now;
}
}
@@ -0,0 +1,284 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class FlipNumber : Viewport3D
{
private bool _isLoaded;
private TextBlock _page1TextDown;
private TextBlock _page2TextUp;
private TextBlock _page2TextDown;
private TextBlock _page3TextUp;
private ContainerUIElement3D _page1;
private ContainerUIElement3D _page2;
private ContainerUIElement3D _page3;
private ContainerUIElement3D _content;
private readonly AxisAngleRotation3D _pageRotation3D;
private readonly DoubleAnimation _animation;
private bool _isAnimating;
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius", typeof(CornerRadius), typeof(FlipNumber), new PropertyMetadata(new CornerRadius(4)));
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
"Background", typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
public Brush Background
{
get => (Brush)GetValue(BackgroundProperty);
set => SetValue(BackgroundProperty, value);
}
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
"Foreground", typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
public Brush Foreground
{
get => (Brush)GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
"FontSize", typeof(double), typeof(FlipNumber), new PropertyMetadata(70.0));
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
"Number", typeof(int), typeof(FlipNumber), new PropertyMetadata(ValueBoxes.Int0Box, OnNumberChanged));
private static void OnNumberChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) =>
((FlipNumber)s).OnNumberChanged();
public int Number
{
get => (int)GetValue(NumberProperty);
set => SetValue(NumberProperty, value);
}
public FlipNumber()
{
var visual3D = new ModelVisual3D
{
Content = new DirectionalLight()
};
Children.Add(visual3D);
_pageRotation3D = new AxisAngleRotation3D
{
Angle = 0,
Axis = new Vector3D(1, 0, 0)
};
_animation = new DoubleAnimation(0, 180, new Duration(TimeSpan.FromSeconds(0.8)))
{
FillBehavior = FillBehavior.Stop
};
_animation.Completed += Animation_Completed;
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
InitNumber();
var transform3D = new RotateTransform3D(_pageRotation3D);
_page2.Transform = transform3D;
};
}
private void Animation_Completed(object sender, EventArgs e)
{
_isAnimating = false;
UpdateNumber();
}
private void InitNumber()
{
_page1 = new ContainerUIElement3D();
var num1 = Number > 8 ? 0 : Number + 1;
var page1NumDown = CreateNumber(num1, false, out _page1TextDown);
_page1.Children.Add(page1NumDown);
_page2 = new ContainerUIElement3D();
var page2NumUp = CreateNumber(num1, true, out _page2TextUp);
var page2NumDown = CreateNumber(Number, false, out _page2TextDown);
_page2.Children.Add(page2NumUp);
_page2.Children.Add(page2NumDown);
_page3 = new ContainerUIElement3D();
var page3NumUp = CreateNumber(Number, true, out _page3TextUp);
_page3.Children.Add(page3NumUp);
var transform3D = new RotateTransform3D(new AxisAngleRotation3D
{
Angle = 180,
Axis = new Vector3D(1, 0, 0)
});
_page3.Transform = transform3D;
_content = new ContainerUIElement3D
{
Children =
{
_page1,
_page2,
_page3
}
};
Children.Add(_content);
}
private bool CheckNull() => _page1TextDown != null && _page2TextUp != null && _page2TextDown != null && _page3TextUp != null;
private void OnNumberChanged()
{
if (!CheckNull()) return;
InitNewNumber();
if (_isAnimating)
{
_isAnimating = false;
UpdateNumber();
return;
}
_isAnimating = true;
_pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);
}
private void InitNewNumber()
{
var num1 = Number.ToString();
_page1TextDown.Text = num1;
_page2TextUp.Text = num1;
}
private void UpdateNumber()
{
_pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, null);
_pageRotation3D.Angle = 0;
var num = Number.ToString();
_page2TextDown.Text = num;
_page3TextUp.Text = num;
_isAnimating = false;
}
private Viewport2DVisual3D CreateNumber(int num, bool isUp, out TextBlock textBlock)
{
int flag;
var rotateTransform = new RotateTransform();
if (isUp)
{
flag = -1;
rotateTransform.Angle = 180;
}
else
{
flag = 1;
}
var halfWidth = ActualWidth / 2;
var quarterWidth = ActualWidth / 4;
var quarterHeight = ActualHeight / 4;
var meMaterial = new DiffuseMaterial();
Viewport2DVisual3D.SetIsVisualHostMaterial(meMaterial, true);
textBlock = new TextBlock
{
RenderTransformOrigin = new Point(0.5, 0.5),
Foreground = Foreground,
FontSize = FontSize,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
TextAlignment = TextAlignment.Center,
Text = num.ToString(),
RenderTransform = rotateTransform,
Margin = new Thickness(0, 0, 0, -quarterHeight),
FontFamily = new FontFamily("Consolas")
};
var border = new Border
{
ClipToBounds = true,
CornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0),
Background = Background,
Width = halfWidth,
Height = quarterHeight,
Child = textBlock
};
var positions = new Point3DCollection
{
new Point3D(-quarterWidth * flag, quarterHeight, 0),
new Point3D(-quarterWidth * flag, 0, 0),
new Point3D(quarterWidth * flag, 0, 0),
new Point3D(quarterWidth * flag, quarterHeight, 0)
};
var triangleIndices = new Int32Collection
{
0,
1,
2,
0,
2,
3
};
var textureCoordinates = new PointCollection
{
new Point(0, 0),
new Point(0, 1),
new Point(1, 1),
new Point(1, 0)
};
var geometry3D = new MeshGeometry3D
{
Positions = positions,
TriangleIndices = triangleIndices,
TextureCoordinates = textureCoordinates
};
var child = new Viewport2DVisual3D
{
Geometry = geometry3D,
Visual = border,
Material = meMaterial
};
return child;
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Windows.Controls;
namespace HandyControl.Controls
{
/// <summary>
/// 作为刻度使用的文字块
/// </summary>
internal class SpeTextBlock : TextBlock
{
public double X { get; set; }
public SpeTextBlock() => Width = 60;
public SpeTextBlock(double x) : this()
{
X = x;
Canvas.SetLeft(this, X);
}
/// <summary>
/// 时间
/// </summary>
private DateTime _time;
/// <summary>
/// 时间
/// </summary>
public DateTime Time
{
get => _time;
set
{
_time = value;
Text = $"{value.ToString(TimeFormat)}\r\n|";
}
}
/// <summary>
/// 时间格式
/// </summary>
public string TimeFormat { get; set; } = "HH:mm";
/// <summary>
/// 横向移动
/// </summary>
/// <param name="offsetX"></param>
public void MoveX(double offsetX) => Canvas.SetLeft(this, X + offsetX);
}
}
@@ -0,0 +1,475 @@
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
{
/// <summary>
/// 时间条
/// </summary>
[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
/// <summary>
/// 是否显示刻度字符串
/// </summary>
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);
}
/// <summary>
/// 刻度字符串
/// </summary>
internal static readonly DependencyProperty SpeStrProperty = DependencyProperty.Register(
"SpeStr", typeof(string), typeof(TimeBar), new PropertyMetadata(Properties.Langs.Lang.Interval1h));
/// <summary>
/// 选中时间
/// </summary>
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);
}
}
/// <summary>
/// 时间改变事件
/// </summary>
public static readonly RoutedEvent TimeChangedEvent =
EventManager.RegisterRoutedEvent("TimeChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<DateTime>>), typeof(TimeBar));
/// <summary>
/// 刻度集合
/// </summary>
private readonly List<SpeTextBlock> _speBlockList = new List<SpeTextBlock>();
/// <summary>
/// 初始化时时间
/// </summary>
private readonly DateTime _starTime;
/// <summary>
/// 时间段集合
/// </summary>
private readonly List<int> _timeSpeList = new List<int>
{
7200000,
3600000,
1800000,
600000,
300000,
60000,
30000
};
/// <summary>
/// 顶部border是否被按下
/// </summary>
private bool _borderTopIsMouseLeftButtonDown;
/// <summary>
/// 控件是否处于拖动中
/// </summary>
private bool _isDragging;
/// <summary>
/// 刻度单项宽度
/// </summary>
private double _itemWidth;
/// <summary>
/// 鼠标按下拖动时选中的时间
/// </summary>
private DateTime _mouseDownTime;
/// <summary>
/// 显示的刻度数目
/// </summary>
private int _speCount = 13;
/// <summary>
/// 刻度区间编号
/// </summary>
private int _speIndex = 1;
/// <summary>
/// 刻度单次偏移
/// </summary>
private double _tempOffsetX;
/// <summary>
/// 刻度总偏移
/// </summary>
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();
}
/// <summary>
/// 是否显示刻度字符串
/// </summary>
public bool ShowSpeStr
{
get => (bool)GetValue(ShowSpeStrProperty);
set => SetValue(ShowSpeStrProperty, value);
}
/// <summary>
/// 刻度字符串
/// </summary>
internal string SpeStr
{
get => (string)GetValue(SpeStrProperty);
set => SetValue(SpeStrProperty, value);
}
/// <summary>
/// 选中时间
/// </summary>
internal DateTime SelectedTime
{
get => (DateTime)GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
/// <summary>
/// 刻度区间编号
/// </summary>
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;
}
}
/// <summary>
/// 时间改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<DateTime>> TimeChanged
{
add => AddHandler(TimeChangedEvent, value);
remove => RemoveHandler(TimeChangedEvent, value);
}
/// <summary>
/// 设置刻度时间格式
/// </summary>
/// <param name="format"></param>
private void SetSpeTimeFormat(string format)
{
foreach (var item in _speBlockList)
item.TimeFormat = format;
}
/// <summary>
/// 设置显示的时间
/// </summary>
/// <param name="time"></param>
public void SetShowTime(DateTime time)
{
if (_isDragging || _borderTopIsMouseLeftButtonDown) return;
SelectedTime = time;
_totalOffsetX = (_starTime - SelectedTime).TotalMilliseconds / _timeSpeList[SpeIndex] * _itemWidth;
Update();
TimeBar_OnMouseMove(null, null);
}
/// <summary>
/// 更新
/// </summary>
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]);
}
/// <summary>
/// 时间转换
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// 鼠标滚轮滚动时改变刻度区间
/// </summary>
/// <param name="e"></param>
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<DateTime>(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<DateTime>(TimeChangedEvent, this)
{
Info = SelectedTime
});
}
}
}
}