WPF 时钟示例

WPF 时钟示例

一.首先添加一个自定义控件 Clock
导入两个引用:Microsoft.Expression.Controls.dll 和 Microsoft.Expression.Drawing.dll
Generic.xaml代码:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPF0316a"
    xmlns:ec="http://schemas.microsoft.com/expression/2010/controls"
    xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing">

    <Style x:Key="temp" TargetType="{x:Type local:Clock}">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="UseLayoutRounding" Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Clock}">
                    <Border Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                            UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
                        <Grid>
                            <ed:Arc x:Name="PART_IncreaseCircle" ArcThickness="3" ArcThicknessUnit="Pixel" EndAngle="360"
                                    Fill="#424658" StartAngle="0" Stretch="None" />
                            
                            <ec:PathListBox x:Name="ShoartTick" IsHitTestVisible="False"
                                            ItemsSource="{TemplateBinding ShortTicks}">
                                <ec:PathListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Border Width="1" Height="7" Background="#3B4053" SnapsToDevicePixels="True"
                                                UseLayoutRounding="True" />
                                    </DataTemplate>
                                </ec:PathListBox.ItemTemplate>
                                <ec:PathListBox.LayoutPaths>
                                    <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                   SourceElement="{Binding ElementName=ShortTickPath}" />
                                </ec:PathListBox.LayoutPaths>
                            </ec:PathListBox>

                            
                            <ec:PathListBox x:Name="LongTick" IsHitTestVisible="False"
                                            ItemsSource="{TemplateBinding LongTicks}">
                                <ec:PathListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Border Width="3" Height="3" Background="White" CornerRadius="100"
                                                SnapsToDevicePixels="True" UseLayoutRounding="True" />
                                    </DataTemplate>
                                </ec:PathListBox.ItemTemplate>
                                <ec:PathListBox.LayoutPaths>
                                    <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                   SourceElement="{Binding ElementName=LongTickPath}" />
                                </ec:PathListBox.LayoutPaths>
                            </ec:PathListBox>

                            
                            <ec:PathListBox x:Name="Number" IsHitTestVisible="False"
                                            ItemsSource="{TemplateBinding NumberList}">
                                <ec:PathListBox.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Foreground="White" RenderTransformOrigin="0.5, 0.5"
                                                   Text="{Binding Item1}">
                                            <TextBlock.RenderTransform>
                                                <RotateTransform Angle="{Binding Item2}" />
                                            </TextBlock.RenderTransform>
                                        </TextBlock>
                                    </DataTemplate>
                                </ec:PathListBox.ItemTemplate>
                                <ec:PathListBox.LayoutPaths>
                                    <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                   SourceElement="{Binding ElementName=NumberPath}" />
                                </ec:PathListBox.LayoutPaths>
                            </ec:PathListBox>

                            <ed:Arc x:Name="LongTickPath" Margin="2" ArcThickness="0" ArcThicknessUnit="Pixel"
                                    EndAngle="0" StartAngle="30" Stretch="None" />

                            <ed:Arc x:Name="ShortTickPath" Margin="5" ArcThickness="0" ArcThicknessUnit="Pixel"
                                    EndAngle="360" StartAngle="0" Stretch="None" />

                            <ed:Arc x:Name="NumberPath" Margin="20" ArcThickness="0" ArcThicknessUnit="Pixel"
                                    EndAngle="0" StartAngle="30" Stretch="None" />

                            <ed:Arc x:Name="PART_SecondCircle" ArcThickness="3" ArcThicknessUnit="Pixel" Fill="#FFF"
                                    StartAngle="0" Stretch="None" />
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                              Content="{TemplateBinding Content}"
                                              ContentTemplate="{TemplateBinding ContentTemplate}" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="{x:Type local:Clock}">
        <Setter Property="Width" Value="200" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Stretch" />
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="UseLayoutRounding" Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Clock}">
                    <Viewbox Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Border Width="200" Height="200" CornerRadius="1000"
                                Padding="{TemplateBinding Padding}"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
                            <Grid>
                                <ed:Arc x:Name="PART_IncreaseCircle" ArcThickness="3" ArcThicknessUnit="Pixel" EndAngle="360"
                                        Fill="#424658" StartAngle="0" Stretch="None" />
                                <ed:Arc ArcThickness="1" ArcThicknessUnit="Pixel" EndAngle="360"
                                        Fill="#424658" StartAngle="0" Stretch="None" Margin="5" />

                                <ec:PathListBox x:Name="ShoartTick" IsHitTestVisible="False"
                                                ItemsSource="{TemplateBinding ShortTicks}">
                                    <ec:PathListBox.ItemTemplate>
                                        <DataTemplate>
                                            <Border Width="2" Height="2" Background="#000" CornerRadius="100"
                                                    SnapsToDevicePixels="True" UseLayoutRounding="True" />
                                        </DataTemplate>
                                    </ec:PathListBox.ItemTemplate>
                                    <ec:PathListBox.LayoutPaths>
                                        <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                       SourceElement="{Binding ElementName=ShortTickPath}" />
                                    </ec:PathListBox.LayoutPaths>
                                </ec:PathListBox>


                                <ec:PathListBox x:Name="LongTick" IsHitTestVisible="False"
                                                ItemsSource="{TemplateBinding LongTicks}">
                                    <ec:PathListBox.ItemTemplate>
                                        <DataTemplate>
                                            <Border Width="4" Height="4" Background="#000" CornerRadius="100"
                                                    SnapsToDevicePixels="True" UseLayoutRounding="True" />
                                        </DataTemplate>
                                    </ec:PathListBox.ItemTemplate>
                                    <ec:PathListBox.LayoutPaths>
                                        <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                       SourceElement="{Binding ElementName=LongTickPath}" />
                                    </ec:PathListBox.LayoutPaths>
                                </ec:PathListBox>


                                <ec:PathListBox x:Name="Number" IsHitTestVisible="False"
                                                ItemsSource="{TemplateBinding NumberList}">
                                    <ec:PathListBox.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Foreground="Black" RenderTransformOrigin="0.5, 0.5"
                                                       Text="{Binding Item1}">
                                                <TextBlock.RenderTransform>
                                                    <RotateTransform Angle="{Binding Item2}" />
                                                </TextBlock.RenderTransform>
                                            </TextBlock>
                                        </DataTemplate>
                                    </ec:PathListBox.ItemTemplate>
                                    <ec:PathListBox.LayoutPaths>
                                        <ec:LayoutPath Distribution="Even" Orientation="OrientToPath"
                                                       SourceElement="{Binding ElementName=NumberPath}" />
                                    </ec:PathListBox.LayoutPaths>
                                </ec:PathListBox>

                                <ed:Arc x:Name="LongTickPath" Margin="12" ArcThickness="0" ArcThicknessUnit="Pixel"
                                        EndAngle="0" StartAngle="30" Stretch="None" />

                                <ed:Arc x:Name="ShortTickPath" Margin="13" ArcThickness="0" ArcThicknessUnit="Pixel"
                                        EndAngle="360" StartAngle="0" Stretch="None" />

                                <ed:Arc x:Name="NumberPath" Margin="30" ArcThickness="0" ArcThicknessUnit="Pixel"
                                        EndAngle="0" StartAngle="30" Stretch="None" />

                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                                  Content="{TemplateBinding Content}"
                                                  ContentTemplate="{TemplateBinding ContentTemplate}" />


                                <Border Width="3" Height="50" Background="Black" RenderTransformOrigin="0.5 1">
                                    <Border.RenderTransform>
                                        <TransformGroup>
                                            <RotateTransform Angle="{Binding HourAngleInner, RelativeSource={RelativeSource AncestorType=local:Clock}}" CenterY="0" />
                                            <TranslateTransform Y="-25" />
                                        </TransformGroup>
                                    </Border.RenderTransform>
                                </Border>

                                <Border Width="2" Height="60" Background="Blue" RenderTransformOrigin="0.5 1">
                                    <Border.RenderTransform>
                                        <TransformGroup>
                                            <RotateTransform Angle="{Binding MinuteAngleInner, RelativeSource={RelativeSource AncestorType=local:Clock}}" CenterY="0" />
                                            <TranslateTransform Y="-30" />
                                        </TransformGroup>
                                    </Border.RenderTransform>
                                </Border>

                                <Border Width="2" Height="80" Background="Red" RenderTransformOrigin="0.5, 1">
                                    <Border.RenderTransform>
                                        <TransformGroup>
                                            <RotateTransform Angle="{Binding SecondAngleInner, RelativeSource={RelativeSource AncestorType=local:Clock}}" CenterY="0" />
                                            <TranslateTransform Y="-40" />
                                        </TransformGroup>
                                    </Border.RenderTransform>
                                </Border>

                                <Border Width="7" Height="7" BorderBrush="Black" Background="White" BorderThickness="2" CornerRadius="100" />
                            </Grid>
                        </Border>
                    </Viewbox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Clock.cs代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace WPF0316a
{
    public class Clock : ContentControl
    {
        #region private fields

        private DispatcherTimer mSecondTimer;

        #endregion

        #region DependencyProperty

        #region Hour

        public string Hour
        {
            get { return (string)GetValue(HourProperty); }
            set { SetValue(HourProperty, value); }
        }

        public static readonly DependencyProperty HourProperty =
            DependencyProperty.Register("Hour", typeof(string), typeof(Clock));

        #endregion

        #region Minute

        public string Minute
        {
            get { return (string)GetValue(MinuteProperty); }
            set { SetValue(MinuteProperty, value); }
        }

        public static readonly DependencyProperty MinuteProperty =
            DependencyProperty.Register("Minute", typeof(string), typeof(Clock));

        #endregion

        #region Second

        public string Second
        {
            get { return (string)GetValue(SecondProperty); }
            set { SetValue(SecondProperty, value); }
        }

        public static readonly DependencyProperty SecondProperty =
            DependencyProperty.Register("Second", typeof(string), typeof(Clock));

        #endregion

        #endregion

        #region Private DependencyProperty

        #region ShortTicks 短刻度线集合
        /// <summary>
        /// 短刻度线依赖属性,用于Binding
        /// </summary>
        public static readonly DependencyProperty ShortTicksProperty =
            DependencyProperty.Register(
                "ShortTicks",
                typeof(IList<object>),
                typeof(Clock),
                new PropertyMetadata(null));

        /// <summary>
        /// 获取或设置短刻度线,用于绑定PathListBox的ItemsSource
        /// </summary>
        /// <value>短刻度线.</value>
        public IList<object> ShortTicks
        {
            get { return (IList<object>)GetValue(ShortTicksProperty); }
            private set { SetValue(ShortTicksProperty, value); }
        }
        #endregion

        #region LongTicks 长刻度线集合
        /// <summary>
        /// 长刻度线依赖属性,用于Binding
        /// </summary>
        public static readonly DependencyProperty LongTicksProperty =
            DependencyProperty.Register(
                "LongTicks",
                typeof(IList<object>),
                typeof(Clock),
                new PropertyMetadata(null));

        /// <summary>
        /// 获取或设置长刻度线,用于绑定PathListBox的ItemsSource
        /// </summary>
        /// <value>长刻度线.</value>
        public IList<object> LongTicks
        {
            get { return (IList<object>)GetValue(LongTicksProperty); }
            private set { SetValue(LongTicksProperty, value); }
        }
        #endregion

        #region NumberList 长刻度线上显示的数字
        /// <summary>
        /// 长刻度线依赖属性,用于Binding
        /// </summary>
        public static readonly DependencyProperty NumberListProperty =
            DependencyProperty.Register(
                "NumberList",
                typeof(IList<Tuple<object, double>>),
                typeof(Clock),
                new PropertyMetadata(null));

        /// <summary>
        /// 获取或设置长刻度线,用于绑定PathListBox的ItemsSource
        /// </summary>
        /// <value>长刻度线.</value>
        public IList<Tuple<object, double>> NumberList
        {
            get { return (IList<Tuple<object, double>>)GetValue(NumberListProperty); }
            private set { SetValue(NumberListProperty, value); }
        }
        #endregion

        #region HourAngleInner

        public double HourAngleInner
        {
            get { return (double)GetValue(HourAngleInnerProperty); }
            private set { SetValue(HourAngleInnerProperty, value); }
        }

        public static readonly DependencyProperty HourAngleInnerProperty =
            DependencyProperty.Register("HourAngleInner", typeof(double), typeof(Clock), new PropertyMetadata(0d));

        #endregion

        #region MinuteAngleInner

        public double MinuteAngleInner
        {
            get { return (double)GetValue(MinuteAngleInnerProperty); }
            private set { SetValue(MinuteAngleInnerProperty, value); }
        }

        public static readonly DependencyProperty MinuteAngleInnerProperty =
            DependencyProperty.Register("MinuteAngleInner", typeof(double), typeof(Clock), new PropertyMetadata(0d));

        #endregion

        #region SecondAngleInner

        public double SecondAngleInner
        {
            get { return (double)GetValue(SecondAngleInnerProperty); }
            private set { SetValue(SecondAngleInnerProperty, value); }
        }

        public static readonly DependencyProperty SecondAngleInnerProperty =
            DependencyProperty.Register("SecondAngleInner", typeof(double), typeof(Clock), new PropertyMetadata(0d));

        #endregion

        #endregion

        #region Constructors

        static Clock()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
        }

        #endregion

        #region Override

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.SetTicks();

            if (this.mSecondTimer == null)
            {
                this.mSecondTimer = new DispatcherTimer();
                this.mSecondTimer.Interval = new TimeSpan(0, 0, 0, 1); ;
                this.mSecondTimer.Tick += MSecondTimer_Tick; ;
            }

            //int millisecond = DateTime.Now.Millisecond;
            //System.Threading.Thread.Sleep(1000 - millisecond);
            this.mSecondTimer.Start();
            this.SetAngle();
        }

        #endregion

        #region private function

        private void SetTicks()
        {
            List<Tuple<object, double>> numbers = new List<Tuple<object, double>>();
            List<object> shortticks = new List<object>();
            List<object> longticks = new List<object>();

            for (int i = 1; i <= 12; i++)
            {
                double angle = -(360 / 12); //一圈360度,分12个时钟刻度
                numbers.Add(new Tuple<object, double>(i, i * angle));
                longticks.Add(new object());
            }

            for (int i = 0; i < 60; i++)
            {
                shortticks.Add(new object());
            }

            this.ShortTicks = shortticks;
            this.LongTicks = longticks;
            this.NumberList = numbers;
        }

        private void SetAngle()
        {
            int hour = DateTime.Now.Hour;
            int minute = DateTime.Now.Minute;
            int second = DateTime.Now.Second;
            if (hour == 0)
            {
                this.HourAngleInner = 360;
            }
            else
            {
                this.HourAngleInner = hour % 12 * 30 + (double)minute / 60 * 30;
            }

            if (minute == 0)
            {
                this.MinuteAngleInner = 360;
            }
            else
            {
                this.MinuteAngleInner = minute * 6;
            }

            if (second == 0)
            {
                this.SecondAngleInner = 360;
            }
            else
            {
                this.SecondAngleInner = second * 6;
            }
            this.Hour = hour >= 10 ? hour.ToString() : "0" + hour;
            this.Minute = minute >= 10 ? minute.ToString() : "0" + minute;
            this.Second = second >= 10 ? second.ToString() : "0" + second;
        }

        #endregion

        #region Event Implement Function

        private void MSecondTimer_Tick(object sender, EventArgs e)
        {
            this.SetAngle();
        }

        #endregion
    }
}

主窗体Xaml代码:

<Window x:Class="WPF0316a.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF0316a"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:Clock x:Name="clock" Padding="0" Background="AntiqueWhite">
            <local:Clock.ContentTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="60">
                        <TextBlock FontFamily="Agency FB" FontSize="16" Foreground="Black"
                                   Text="{Binding Hour, ElementName=clock}" />
                        <TextBlock FontFamily="Agency FB" FontSize="16" Foreground="Black" Text=":" />
                        <TextBlock FontFamily="Agency FB" FontSize="16" Foreground="Black"
                                   Text="{Binding Minute, ElementName=clock}" />
                        <TextBlock FontFamily="Agency FB" FontSize="16" Foreground="Black" Text=":" />
                        <TextBlock FontFamily="Agency FB" FontSize="16" Foreground="Black"
                                   Text="{Binding Second, ElementName=clock}" />
                    </StackPanel>
                </DataTemplate>
            </local:Clock.ContentTemplate>
        </local:Clock>
    </Grid>
</Window>

运行效果:
在这里插入图片描述
源代码库下载地址:https://download.csdn.net/download/qq_43024228/12251111

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WPF(Microsoft Windows Presentation Foundation)是一种用于创建图形化用户界面的技术。MVVM(Model-View-ViewModel)是一种软件设计模式,用于将用户界面的逻辑与数据分离。WPF MVVM示例是一种将WPF和MVVM结合起来的示例项目,以演示如何使用这两个技术来构建用户界面。 在WPF MVVM示例中,通常会有三个主要的组成部分:Model、View和ViewModel。Model用于表示数据模型,即应用程序中的业务逻辑和数据。View用于表示用户界面,即显示数据和与用户交互的部分。ViewModel则是连接Model和View的桥梁,负责处理数据和业务逻辑,并将其绑定到View上。 在WPF MVVM示例中,首先需要创建一个Model,该Model包含应用程序需要使用的数据和方法。然后,创建一个View,该View负责展示数据和与用户交互,通常是通过XAML来构建用户界面。接下来,创建一个ViewModel,该ViewModel将负责处理数据和业务逻辑,并将其绑定到View上。 ViewModel通常会包含一些属性,用于存储数据,并通过数据绑定将这些数据展示在View上。ViewModel还会包含一些命令(Command),用于处理用户的操作,并根据需要更新数据。ViewModel还可以使用一种叫做INotifyPropertyChanged的接口,以实现数据的双向绑定,即当数据发生变化时,自动更新View上的数据。 WPF MVVM示例还可以包含一些其他的功能,比如使用容器控件(如ListBox、TreeView等)来展示数据列表或树状结构,使用验证机制来确保用户输入的有效性,使用消息机制来实现模块间的通信等。 总之,WPF MVVM示例是一种通过使用WPF技术和MVVM设计模式来构建用户界面的示例项目。它可以帮助开发人员更好地组织和管理代码,提高代码的可维护性和可扩展性,并提供更好的用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值