制作简单的WPF时钟
原文:制作简单的WPF时钟
在很早之前,我曾经写过一个GDI+的时钟,见“C#时钟控件 (C# Clock Control)” http://blog.csdn.net/johnsuna/archive/2006/02/13/597746.aspx及“使用C#编写LED样式时钟控件”(http://blog.csdn.net/johnsuna/archive/2006/02/14/598867.aspx),进入WPF时代了,如何用WPF绘制一个时钟呢?
先看效果:
上面显示的是时间值,下面是图形版的时钟。
制作要点:
1. 首先在Expression Blend中画出时钟的样式;
2. 建立时钟的程序辅助类;
3. 将此时钟样式需要动态换掉的部分改成相应的绑定值。
由于具体步骤很多,这里只说说技术难点和要点,着重说明上述第2点部分。
// 时钟控件类:Clock.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Timers;
using System.Windows.Threading;
namespace BrawDraw.Com.WPF.Clock.ControlLibrary
{
/// <summary>Clock Control
/// </summary>
public class Clock : Control
{
private DispatcherTimer timer;
static Clock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
timer = new DispatcherTimer();
timer.Tick += new EventHandler(Timer_Tick);
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
UpdateDateTime();
timer.Interval = TimeSpan.FromMilliseconds(1000 - DateTime.Now.Millisecond);
}
private void UpdateDateTime()
{
this.DateTime = System.DateTime.Now;
}
#region DateTime property
public DateTime DateTime
{
get
{
return (DateTime)GetValue(DateTimeProperty);
}
private set
{
SetValue(DateTimeProperty, value);
}
}
public static DependencyProperty DateTimeProperty = DependencyProperty.Register(
"DateTime",
typeof(DateTime),
typeof(Clock),
new PropertyMetadata(DateTime.Now, new PropertyChangedCallback(OnDateTimeInvalidated)));
public static readonly RoutedEvent DateTimeChangedEvent =
EventManager.RegisterRoutedEvent("DateTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime>), typeof(Clock));
protected virtual void OnDateTimeChanged(DateTime oldValue, DateTime newValue)
{
RoutedPropertyChangedEventArgs<DateTime> args = new RoutedPropertyChangedEventArgs<DateTime>(oldValue, newValue);
args.RoutedEvent = Clock.DateTimeChangedEvent;
RaiseEvent(args);
}
private static void OnDateTimeInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Clock clock = (Clock)d;
DateTime oldValue = (DateTime)e.OldValue;
DateTime newValue = (DateTime)e.NewValue;
clock.OnDateTimeChanged(oldValue, newValue);
}
#endregion
}
}
// 时钟内部时针、分针、秒针角度计算及星期显示的类: ClockConverters.cs
// 由于WPF中旋转角度是以度单位,计算为绕一个圆周时,为360度。所以,计算时以360度来计算。
using System;
using System.Globalization;
using System.Windows.Data;
namespace BrawDraw.Com.WPF.Clock
{
// 一分钟60秒,一周为360度,所以,一秒钟就占6度,所以是:秒数×6。
[ValueConversion(typeof(DateTime), typeof(int))]
public class SecondsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.Second * 6;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
// 一小时是60分钟,一周为360度,所以,一分钟就占6度,所以是:分钟数×6。
[ValueConversion(typeof(DateTime), typeof(int))]
public class MinutesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.Minute * 6;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
// 时钟上显示12小时,一周为360度,这样,每小时占30度,考虑分钟所占角度是分钟数/2,所以结果是:小时数×30 + 分钟数/2。
[ValueConversion(typeof(DateTime), typeof(int))]
public class HoursConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return (date.Hour * 30) + (date.Minute / 2);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
[ValueConversion(typeof(DateTime), typeof(string))]
public class WeekdayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.DayOfWeek.ToString().Substring(0,3);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
剩下的就十分简单,程序的显示部分(XAML代码):
// MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customControl="clr-namespace:BrawDraw.Com.WPF.Clock.ControlLibrary;assembly=CustomControlLibrary"
xmlns:local="clr-namespace:BrawDraw.Com.WPF.Clock"
x:Class="BrawDraw.Com.WPF.Clock.Window1"
Title="WPF Clock" Height="394" Width="332"
>
<StackPanel HorizontalAlignment="Center">
<StackPanel.Resources>
<local:SecondsConverter x:Key="secondsConverter"/>
<local:MinutesConverter x:Key="minutesConverter"/>
<local:HoursConverter x:Key="hoursConverter"/>
<local:WeekdayConverter x:Key="weekdayConverter"/>
<Style x:Key="AnalogClock" TargetType="{x:Type customControl:Clock}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customControl:Clock}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="108" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="108" />
</Grid.RowDefinitions>
<Ellipse Width="108" Height="108" StrokeThickness="3" Grid.Row="0" Grid.Column="0">
<Ellipse.Stroke>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="DarkBlue" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
<Ellipse VerticalAlignment="Center" HorizontalAlignment="Center" Width="104" Height="104" StrokeThickness="3">
<Ellipse.Stroke>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="DarkBlue" Offset="0" />
<GradientStop Color="LightBlue" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Ellipse.Stroke>
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.96,0.972" StartPoint="0.017,0.03">
<GradientStop Color="#FFFAF9F6" Offset="0"/>
<GradientStop Color="#FF539BDF" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,30" VerticalAlignment="Bottom" FontSize="10" Name="MonthDay" Text="{Binding Path=DateTime, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource weekdayConverter}}"/>
<!-- 下面这一部分有待改进,使用C#代码控制起来会更方便,且更有利于代码的维护 -->
<Canvas Width="102" Height="102">
<Ellipse Width="8" Height="8" Fill="Black" Canvas.Top="46" Canvas.Left="46" />
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="0" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="30" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="60" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="90" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="120" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="150" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="180" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="210" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="240" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="48" Fill="Black" Width="4" Height="8">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="270" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="300" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Top="5" Canvas.Left="49" Fill="Black" Width="2" Height="6">
<Rectangle.RenderTransform>
<RotateTransform CenterX="2" CenterY="46" Angle="330" />
</Rectangle.RenderTransform>
</Rectangle>
<!-- 时钟、分针和秒针的显示 -->
<Rectangle x:Name="HourHand" Canvas.Top="21" Canvas.Left="48"
Fill="Black" Width="4" Height="30">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Path=DateTime, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource hoursConverter}}" CenterX="2" CenterY="30"></RotateTransform>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="MinuteHand" Canvas.Top="10" Canvas.Left="51"
Fill="DarkOrange" Width="2" Height="40">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Path=DateTime, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource minutesConverter}}" CenterX="1" CenterY="40"></RotateTransform>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="SecondHand" Canvas.Top="4" Canvas.Left="49"
Fill="Red" Width="1" Height="47">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Path=DateTime, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource secondsConverter}}" CenterX="0.5" CenterY="47"></RotateTransform>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<customControl:Clock x:Name="customControlClock" FontFamily="Arial" />
<customControl:Clock x:Name="customControlAnalogClock" Style="{StaticResource AnalogClock}" RenderTransformOrigin="0.388,0.66" Width="121" Height="112" />
</StackPanel>
</Window>
(已用粗体标注重要的部分)
比较一下使用GDI+和WPF的方便性,从设计的角度来讲,WPF显得更方便,更快捷。从编程的代码量相比,两者差别不是太多,我统计了一下总体代码量,GDI+甚至比WPF更少。运行效率方面,暂未做测试。
制作简单的WPF时钟的更多相关文章
- 制作WPF时钟之2
原文:制作WPF时钟之2 前段时间写了一篇"制作简单的WPF时钟",今天再制作了一个更漂亮的WPF时钟,目前仅完成了设计部分,准备将它制作成一个无边框窗体式的时钟. 效果图: ...
- 制作一个简单的WPF图片浏览器
原文:制作一个简单的WPF图片浏览器 注:本例选自MSDN样例,并略有改动.先看效果: 这里实现了以下几个功能:1. 对指定文件夹下所有JPG文件进行预览2. 对选定图片进行旋转3. 对选定图片 ...
- ZAM 3D 制作简单的3D字幕 流程(二)
原地址:http://www.cnblogs.com/yk250/p/5663907.html 文中表述仅为本人理解,若有偏差和错误请指正! 接着 ZAM 3D 制作简单的3D字幕 流程(一) .本篇 ...
- C# WPF 时钟动画(1/2)
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. C# WPF 时钟动画(1/2) 内容目录 实现效果 业务场景 编码实现 本文参考 源码下载 ...
- js简单 图片版时钟,带翻转效果
js简单 图片版时钟,带翻转效果 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"& ...
- Highcharts使用教程(1):制作简单图表
今天我们要使用JavaScript图表Highcharts制作简单的柱形图,我们已经安装好Highcharts,让我们开始制作图表吧. 步骤一 在网页中添加一个div.设置id,设置图表长.高.代码如 ...
- phalcon: eventManager事件管理(结合dispatcher调度控制器)制作简单的acl
制作简单的acl, dispatcher(专门用来加载或调度或跳转到相应的url地址即XXXcontroller的调度器或控制器,能够在controller执行前对controller进行停止跳转等) ...
- 在VS中手工创建一个最简单的WPF程序
如果不用VS的WPF项目模板,如何手工创建一个WPF程序呢?我们来模仿WPF模板,创建一个最简单的WPF程序. 第一步:文件——新建——项目——空项目,创建一个空项目. 第二步:添加引用,Presen ...
- canvas制作简单动画
在画布元素<canvas>中,除了绘制图形.图像.文字外,还可以制作一些简单的动画,制作过程十分简单,主要分为两步操作: 1.自定义一个函数,用于图形的移动或其他动作. 2.使用setIn ...
随机推荐
- [Ramda] Pluck & Props: Get the prop(s) from object array
Pluck: Get one prop from the object array: R.pluck(}, {a: }]); //=> [1, 2] R.pluck()([[, ], [, ]] ...
- C#实现人脸识别
C#实现基于ffmpeg加虹软的人脸识别 关于人脸识别 目前的人脸识别已经相对成熟,有各种收费免费的商业方案和开源方案,其中OpenCV很早就支持了人脸识别,在我选择人脸识别开发库时,也横向对比了三种 ...
- [疯狂Java]JDBC:事务管理、中间点、批量更新
1. 数据库事务的概念: 1) 事务的目的就是为了保证数据库中数据的完整性. 2) 设想一个银行转账的过程,假设分两步,第一步是A的账户-1000,第二步是B的账户+1000.这两个动 ...
- Linux 系统 杀Oracle 进程
Linux 系统 杀Oracle 进程 杀掉进程用此方法比较好,能保证杀得干净,而不是用SQL alter system kill kill -9 `ps -ef|grep "oracle ...
- CSS边框作图
原文 简书原文:https://www.jianshu.com/p/537a878304f2 大纲 1.border-style的值 2.利用border作图——基本图形 3.利用border作图—— ...
- 【42.59%】【codeforces 602A】Two Bases
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- [Angular] Test component template
Component: import { Component, Input, ChangeDetectionStrategy, EventEmitter, Output } from '@angular ...
- 立足“快时尚”,联想笋尖S90怎样诠释“比美更美”?
现现在."快时尚"已经成为广受年轻人追捧的消费观,从服装界的H&M.ZARA到餐饮界的绿茶.外婆家等等,我们都不难看出,快时尚已成为激发年轻人消费欲望的核心元素,并 ...
- Sphinx+MySQL5.1x+SphinxSE+mmseg中文分词
什么是Sphinx Sphinx 是一个全文检索引擎,一般而言,Sphinx是一个独立的搜索引擎,意图为其它应用提供快速.低空间占用.高结果相关度的全文搜索功能.Sphinx能够很easy的与SQL数 ...
- linux下的多线程,pthread_create函数
pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int pthread_create(pthread_t*restrict ...