整个逻辑还是很简单的,没有什么正弦余弦计算,没有什么座标计算。就是简单的旋转动画;
源码地址:https://github.com/Super0Lan/Timer.git
exe地址:https://github.com/Super0Lan/ControlDesign/blob/master/DemoList.exe
直接上代码
Xaml代码:
<Window.Resources>
<Style TargetType="ListBox">
<Setter Property="IsHitTestVisible" Value="False"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"></Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"></Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Grid Background="Transparent"></Grid>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ListBoxItem" x:Key="TimeItem">
<Setter Property="IsHitTestVisible" Value="False"></Setter>
<Setter Property="Width" Value="150"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Right"></Setter>
<Setter Property="Height" Value="150"></Setter>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter Content="{Binding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem},Path=DataContext.Angle}"/>
</TransformGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White"></Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ListBoxItem" x:Key="DaysItem" BasedOn="{StaticResource TimeItem}">
<Setter Property="Width" Value="300"></Setter>
<Setter Property="Height" Value="300"></Setter>
</Style>
<Style TargetType="ListBoxItem" x:Key="HoursItem" BasedOn="{StaticResource TimeItem}">
<Setter Property="Width" Value="450"></Setter>
<Setter Property="Height" Value="450"></Setter>
</Style>
<Style TargetType="ListBoxItem" x:Key="MinsItem" BasedOn="{StaticResource TimeItem}">
<Setter Property="Width" Value="600"></Setter>
<Setter Property="Height" Value="600"></Setter>
</Style>
<Style TargetType="ListBoxItem" x:Key="SecondsItem" BasedOn="{StaticResource TimeItem}">
<Setter Property="Width" Value="750"></Setter>
<Setter Property="Height" Value="750"></Setter>
</Style>
</Window.Resources>
<Grid RenderTransformOrigin="0.5,0.5" Background="Transparent">
<ListBox SelectedIndex="{Binding CurrentMonth}" x:Name="MonthItems" SelectionChanged="MonthItems_SelectionChanged" ItemsSource="{Binding Months}" ItemContainerStyle="{StaticResource TimeItem}"></ListBox>
<ListBox SelectedIndex="{Binding CurrentDay}" x:Name="DayItems" SelectionChanged="DayItems_SelectionChanged" ItemsSource="{Binding Days}" ItemContainerStyle="{StaticResource DaysItem}"></ListBox>
<ListBox SelectedIndex="{Binding CurrentHour}" x:Name="HourItems" SelectionChanged="HourItems_SelectionChanged" ItemsSource="{Binding Hours}" ItemContainerStyle="{StaticResource HoursItem}"></ListBox>
<ListBox SelectedIndex="{Binding CurrentMin}" x:Name="MinItems" SelectionChanged="MinItems_SelectionChanged" ItemsSource="{Binding Mins}" ItemContainerStyle="{StaticResource MinsItem}"></ListBox>
<ListBox SelectedIndex="{Binding CurrentSecond}" x:Name="SecondItems" SelectionChanged="SecondItems_SelectionChanged" ItemsSource="{Binding Seconds}" ItemContainerStyle="{StaticResource SecondsItem}"></ListBox>
</Grid>
C# 代码
public partial class MainWindow : Window
{
public TimeItemCollection Data = new TimeItemCollection();
public MainWindow()
{
InitializeComponent();
DataContext = Data;
Timer timer = new Timer((state) =>
{
Dispatcher.Invoke(() =>
{
DateTime date = DateTime.Now;
Data.CurrentMonth = date.Month - 1;
Data.CurrentDay = date.Day - 1;
Data.CurrentHour = date.Hour;
Data.CurrentMin = date.Minute;
Data.CurrentSecond = date.Second;
});
});
timer.Change(0, 100);
}
private void SecondItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Data.CorrectSecond();
SecondItems.BeginRotate(0, 6);
}
}
private void MonthItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Data.CorrectMonth();
MonthItems.BeginRotate(0, 30);
}
}
private void DayItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Data.CorrectDay();
DayItems.BeginRotate(0, 360.0 / 31.0);
}
}
private void HourItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Data.CorrectHour();
HourItems.BeginRotate(0, 15);
}
}
private void MinItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Data.CorrectMin();
MinItems.BeginRotate(0, 6);
}
}
}
public static class UIElementExtensions
{
public static void BeginRotate(this UIElement uIElement, double from, double to, int mili = 500)
{
RotateTransform rtf = new RotateTransform();
uIElement.RenderTransform = rtf;
uIElement.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, new DoubleAnimation(from, to, new Duration(TimeSpan.FromMilliseconds(mili)))
{
BeginTime = new TimeSpan(0),
});
}
}
public class TimeItemCollection : BaseModel
{
public ObservableCollection<TimeItem> Months { get; set; } = new ObservableCollection<TimeItem>();
public ObservableCollection<TimeItem> Days { get; set; } = new ObservableCollection<TimeItem>();
public ObservableCollection<TimeItem> Hours { get; set; } = new ObservableCollection<TimeItem>();
public ObservableCollection<TimeItem> Mins { get; set; } = new ObservableCollection<TimeItem>();
public ObservableCollection<TimeItem> Seconds { get; set; } = new ObservableCollection<TimeItem>();
private int _currentMonth;
public int CurrentMonth { get { return _currentMonth; } set { Set(ref _currentMonth, value); } }
private int _currentDay;
public int CurrentDay { get { return _currentDay; } set { Set(ref _currentDay, value); } }
private int _currentHour;
public int CurrentHour { get { return _currentHour; } set { Set(ref _currentHour, value); } }
private int _currentMin;
public int CurrentMin { get { return _currentMin; } set { Set(ref _currentMin, value); } }
private int _currentSecond;
public int CurrentSecond { get { return _currentSecond; } set { Set(ref _currentSecond, value); } }
public TimeItemCollection()
{
DateTime date = DateTime.Now;
int month = date.Month;
int day = date.Day;
int hour = date.Hour;
int min = date.Minute;
int second = date.Second;
for (int i = 1; i <= 12; i++)
{
Months.Add(new TimeItem()
{
Content = $"{i.ToString().PadLeft(2, '0')}月",
Value = i,
Angle = 30 * (i - month - 1)
});
}
for (int i = 1; i <= 31; i++)
{
Days.Add(new TimeItem()
{
Content = $"{i.ToString().PadLeft(2, '0')}天",
Value = i,
Angle = 360.0 / 31.0 * (i - day - 1)
});
}
for (int i = 0; i < 24; i++)
{
Hours.Add(new TimeItem()
{
Content = $"{i.ToString().PadLeft(2, '0')}点",
Value = i,
Angle = 15 * (i - hour - 1)
});
}
for (int i = 0; i < 60; i++)
{
Mins.Add(new TimeItem()
{
Content = $"{i.ToString().PadLeft(2, '0')}分",
Value = i,
Angle = 6 * (i - min - 1)
});
}
for (int i = 0; i < 60; i++)
{
Seconds.Add(new TimeItem()
{
Content = $"{i.ToString().PadLeft(2, '0')}秒",
Value = i,
Angle = 6 * (i - second - 1)
});
}
}
public void CorrectSecond()
{
int second = DateTime.Now.Second;
Parallel.For(0, 60, (i) =>
{
Seconds[i].Angle = 6 * (i - second -1);
});
}
public void CorrectMonth()
{
int month = DateTime.Now.Month;
Parallel.For(0, 12, (i) =>
{
Months[i].Angle = 30 * (i - month);
});
}
public void CorrectDay()
{
int day = DateTime.Now.Day;
Parallel.For(0, 31, (i) =>
{
Days[i].Angle = 360.0 / 31.0 * (i - day);
});
}
public void CorrectHour()
{
int hour = DateTime.Now.Hour;
Parallel.For(0, 24, (i) =>
{
Hours[i].Angle = 15 * (i - hour - 1);
});
}
public void CorrectMin()
{
int min = DateTime.Now.Minute;
Parallel.For(0, 60, (i) =>
{
Mins[i].Angle = 6 * (i - min - 1);
});
}
}
public class TimeItem : BaseModel
{
public string Content { get; set; }
public int Value { get; set; }
private double _anlge;
public double Angle { get { return _anlge; } set { Set(ref _anlge, value); } }
}
public class BaseModel : INotifyPropertyChanged
{
protected bool Set<T>(ref T field, T newValue = default(T), [CallerMemberName]string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return false;
}
var oldValue = field;
field = newValue;
RaisePropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}