0

I wonder if it is possible to create a digital clock in xaml using animation (without background code tags)

The analog clock can be realized by converting the current time to the angle by matrix conversion, but the digital clock can't be operated like this. I tried a lot of methods, but it didn't work. Does anyone know if there is any good way to implement it?

Analog clock implementation

<Window>
    <Window.Resources>
        <FrameworkElement x:Key="time" Tag={x:Static s:DateTime.Now}/>


        <TransformGroup x:Key="transformHour">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Hour}"
                                Y="{Binding Source={StaticResource time},Path=Tag.Minute}"/>
            <MatrixTransform Matrix="30 0 0.5 0 0 0"/>
        </TransformGroup>

        <TransformGroup x:Key="transformMinute">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Minute}"
                                Y="{Binding Source={StaticResource time},Path=Tag.Second}"/>
            <MatrixTransform Matrix="6 0 0.1 0 0 0"/>
        </TransformGroup>

        <TransformGroup x:Key="transformSecond">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Second}"/>
            <MatrixTransform Matrix="6 0 0 0 0 0"/>
        </TransformGroup>

        <Style TargetType="{x:Type Path}">
            <Setter Property="Stroke" 
                    Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
            <Setter Property="StrokeThickness" Value="3"/>
            <Setter Property="StrokeDashCap" Value="Triangle"/>
        </Style>

    </Window.Resources>

    <Viewbox>
        <Canvas Width="200" Height="200">
            <Canvas.RenderTransform>
                <TranslateTransform X="100" Y="100"/>
            </Canvas.RenderTransform>

            <Path Data="M 0 -90 A 90 90 0 1 1 -0.01 -90"
                  StrokeDashArray="0 3.14157" />

            <Path Data="M 0 -90 A 90 90 0 1 1 -0.01 -90"
                  StrokeDashArray="0 7.854"
                  StrokeThickness="6"/>

            <Border Background="LightBlue" Width="10" Height="80" RenderTransformOrigin="0.5 0">
                <Border.RenderTransform>
                    <TransformGroup>
                        <RotateTransform x:Name="bor_Second" Angle="{Binding Source={StaticResource transformSecond},Path=Value.OffsetX}"/>
                        <RotateTransform Angle="180"/>
                    </TransformGroup>
                </Border.RenderTransform>
            </Border>

            <Border Background="LightGreen" Width="10" Height="60" RenderTransformOrigin="0.5 0">
                <Border.RenderTransform>
                    <TransformGroup>
                        <RotateTransform x:Name="bor_Minute" Angle="{Binding Source={StaticResource transformMinute},Path=Value.OffsetX}"/>
                        <RotateTransform Angle="180"/>
                    </TransformGroup>
                </Border.RenderTransform>
            </Border>

            <Border Background="LightGray" Width="10" Height="40" RenderTransformOrigin="0.5 0">
                <Border.RenderTransform>
                    <TransformGroup>
                        <RotateTransform x:Name="bor_Hour" Angle="{Binding Source={StaticResource transformHour},Path=Value.OffsetX}"/>
                        <RotateTransform Angle="180"/>
                    </TransformGroup>
                </Border.RenderTransform>
            </Border>

        </Canvas>
    </Viewbox>
    <Window.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="bor_Hour"
                                    Storyboard.TargetProperty="Angle"
                                    IsAdditive="True"
                                    Duration="12:0:0"
                                    From="0" To="360"
                                    RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="bor_Minute"
                                    Storyboard.TargetProperty="Angle"
                                    IsAdditive="True"
                                    Duration="1:0:0"
                                    From="0" To="360"
                                    RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="bor_Second"
                                    Storyboard.TargetProperty="Angle"
                                    IsAdditive="True"
                                    Duration="0:1:0"
                                    From="0" To="360"
                                    RepeatBehavior="Forever"
                                    />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>
</Window>

Digital clock (there is an error), I feel that it is more troublesome to use this idea. Does anyone know if there is any good way to implement it?

<Window>
    <Window.Resources>

        <!--current time-->
        <FrameworkElement x:Key="time" Tag="{x:Static s:DateTime.Now}"/>


        <!--Current minutes remaining seconds-->
        <TransformGroup x:Key="transformSecond">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Second}" Y="60"/>
            <MatrixTransform Matrix="-1 0 1 0 0 0"/>
        </TransformGroup>

        <!--Remaining seconds interval-->
        <FrameworkElement x:Key="timeSpanSecond" 
                          Tag="{Binding Source={StaticResource transformSecond},Path=Value.OffsetX,StringFormat={}{0:F0}}"/>


        <!--Current hours remaining minutes-->
        <TransformGroup x:Key="transformMinute">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Minute}" Y="60"/>
            <MatrixTransform Matrix="-1 1 1 0 0 1"/>
        </TransformGroup>

        <!--Remaining minute interval-->
        <FrameworkElement x:Key="timeSpanMinute" 
                          Tag="{Binding Source={StaticResource transformMinute},Path=Value.OffsetX,StringFormat={}{0:F0}}"/>

        <!--Next minute-->
        <FrameworkElement x:Key="minuteNext" 
                          Tag="{Binding Source={StaticResource transformMinute},Path=Value.OffsetY}"/>


        <!--Hours remaining on the day-->
        <TransformGroup x:Key="transformHour">
            <TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Hour}" Y="24"/>
            <MatrixTransform Matrix="-1 1 1 0 0 1"/>
        </TransformGroup>


        <!--Remaining hours interval-->
        <FrameworkElement x:Key="timeSpanHour" 
                          Tag="{Binding Source={StaticResource transformHour},Path=Value.OffsetX,StringFormat={}{0:F0}}"/>

        <!--Next hour-->
        <FrameworkElement x:Key="hourNext" 
                          Tag="{Binding Source={StaticResource transformHour},Path=Value.OffsetY}"/>




    </Window.Resources>

    <Grid>
        <!--Width:Current seconds-->
        <!--Text:Current remaining seconds(TimeSpan)-->
        <TextBlock x:Name="tbk_Second" Visibility="Hidden" 
                   Width="{Binding Source={StaticResource time},Path=Tag.Second}"
                   Text="{Binding Source={StaticResource timeSpanSecond},StringFormat=0:0:{0},Path=Tag}"/>


        <TextBlock x:Name="tbk_Minute" Visibility="Hidden"  
                   Width="{Binding Source={StaticResource time},Path=Tag.Minute}"       
                   Text="{Binding Source={StaticResource timeSpanMinute},StringFormat=0:{0}:0,Path=Tag}">
        </TextBlock>


        <TextBlock x:Name="tbk_Hour" Visibility="Hidden" 
                   Width="{Binding Source={StaticResource time},Path=Tag.Hour}"       
                   Text="{Binding Source={StaticResource timeSpanHour},StringFormat={}{0}:0:0,Path=Tag}"/>

        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">
            <Run Text="{Binding ElementName=tbk_Hour,Path=Width,StringFormat={}{0:F0}}"/>
            <Run Text=":"/>
            <Run Text="{Binding ElementName=tbk_Minute,Path=Width,StringFormat={}{0:F0}}"/>
            <Run Text=":"/>
            <Run Text="{Binding ElementName=tbk_Second,Path=Width,StringFormat={}{0:F0}}"/>
        </TextBlock>


    </Grid>

    <Window.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard >

                    <DoubleAnimation Storyboard.TargetName="tbk_Hour"
                                    Storyboard.TargetProperty="Width"
                                    BeginTime="{Binding ElementName=tbk_Minute,Path=Text}"
                                    Duration="{Binding ElementName=tbk_Hour,Path=Text}"
                                    From="{Binding Source={StaticResource hourNext},Path=Tag}" 
                                    To="23"/>

                    <DoubleAnimation Storyboard.TargetName="tbk_Hour"
                                    Storyboard.TargetProperty="Width"
                                    BeginTime="{Binding ElementName=tbk_Hour,Path=Text}"
                                    Duration="24:0:0"
                                    From="0" 
                                    To="23"        
                                    RepeatBehavior="Forever"/>


                    <DoubleAnimation Storyboard.TargetName="tbk_Minute"
                                    Storyboard.TargetProperty="Width"
                                    BeginTime="{Binding ElementName=tbk_Second,Path=Text}"
                                    Duration="{Binding ElementName=tbk_Minute,Path=Text}"
                                    From="{Binding Source={StaticResource minuteNext},Path=Tag}" 
                                    To="59"/>

                    <DoubleAnimation Storyboard.TargetName="tbk_Minute"
                                    Storyboard.TargetProperty="Width"
                                    BeginTime="{Binding ElementName=tbk_Minute,Path=Text}"
                                    Duration="1:0:0"
                                    From="0" 
                                    To="59"        
                                    RepeatBehavior="Forever"/>


                    <DoubleAnimation 
                                    Storyboard.TargetName="tbk_Second"
                                    Storyboard.TargetProperty="Width"
                                    Duration="{Binding ElementName=tbk_Second,Path=Text}"                                             
                                    From="{Binding Source={StaticResource time},Path=Tag.Second}" 
                                    To="59" 
                                    />
                    <DoubleAnimation Storyboard.TargetName="tbk_Second"
                                    Storyboard.TargetProperty="Width"
                                    BeginTime="{Binding ElementName=tbk_Second,Path=Text}"
                                    Duration="0:1:0"
                                    From="0" 
                                    To="59"        
                                    RepeatBehavior="Forever"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>

</Window>
  • What is "the digital clock"? Please explain how it is supposed to look like and to behave. – Clemens Mar 26 '19 at 06:30
  • I guess the Comment Tag `` instead of ->`. – LittleBit Mar 26 '19 at 06:53
  • @Clemens a clock in which hours, minutes, and sometimes seconds are represented by numbers.It looks like 00:00:00-23:59:59 – Blue Zhang Mar 26 '19 at 07:13
  • @LittleBit Yes, I have corrected this error. – Blue Zhang Mar 26 '19 at 07:14
  • @BlueZhang the easiest way would be to use a textblock bound to a dependency property filled by DateTime.now() and use string format to get the timeformat you want to have this would also be already region specific also I recommend adding this font to you project: https://www.keshikan.net/fonts-e.html – Denis Schaf Mar 26 '19 at 07:29
  • Have you tried to correct your `StringFormat` in the `Run` blocks? (eg. replace `StringFormat={}{0:F0}` with `StringFormat={}{0:00}`) – LittleBit Mar 26 '19 at 09:38

2 Answers2

0

Maybe this is the answer you want :)

<Window x:Class="WpfApp6.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:system="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <Grid.Resources>
            <!--Set x: share to get the latest every time-->
            <system:DateTime x:Key="DateTime"
                            x:Shared="False" />
            <Storyboard x:Key="Storyboard">
                <!--Use keyframe animation to update datetime -->
                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="DataContext"
                                            Duration="0:0:1"
                                            RepeatBehavior="Forever"
                                            AutoReverse="False">
                    <DiscreteObjectKeyFrame KeyTime="50%"
                                            Value="{StaticResource DateTime}" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </Grid.Resources>
        <!--Get datetime from DataContext-->
        <TextBlock Text="{Binding RelativeSource={RelativeSource Self},Path=DataContext.Now}"
                DataContext="{StaticResource DateTime}">
            <TextBlock.Triggers>
                <EventTrigger RoutedEvent="Loaded">
                    <BeginStoryboard Storyboard="{StaticResource Storyboard}" />
                </EventTrigger>
            </TextBlock.Triggers>
        </TextBlock>
    </Grid>
</Window>
ZiYuCai1984
  • 46
  • 1
  • 3
-1

I recommend you do it based on textblocks and a suitable font like this one:

https://www.keshikan.net/fonts-e.html

Your textblocks are bound to propertys that could be set by an async function calling 'DateTime.Now();' here is a XAML-Example:

            <Border HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,40" Background="DarkGray" BorderThickness="1,1,0,0" BorderBrush="Gray">
                <Grid>  
                    <!-- Displays the actual time in red-->
                    <StackPanel Orientation="Horizontal" Margin="2">
                        <TextBlock Opacity="0.6" Foreground="Red" FontSize="20" Text="{Binding Hour, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Opacity="0.6" Foreground="Red" FontSize="20" FontFamily="DSEG7 Classic Mini" >:</TextBlock>
                        <TextBlock Opacity="0.6" Foreground="Red" FontSize="20" Text="{Binding Min, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Opacity="0.6" Foreground="Red" FontSize="20" FontFamily="DSEG7 Classic Mini">:</TextBlock>
                        <TextBlock Opacity="0.6" Foreground="Red" FontSize="20" Text="{Binding Sec, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                    </StackPanel>
                    <!-- Creates a Glow Effect -->
                    <StackPanel Orientation="Horizontal" Margin="2">
                        <StackPanel.Effect>
                            <BlurEffect KernelType="Gaussian" Radius="3"/>
                        </StackPanel.Effect>
                        <TextBlock Foreground="Red" FontSize="20" Text="{Binding Hour, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Foreground="Red" FontSize="20" FontFamily="DSEG7 Classic Mini" >:</TextBlock>
                        <TextBlock Foreground="Red" FontSize="20" Text="{Binding Min, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Foreground="Red" FontSize="20" FontFamily="DSEG7 Classic Mini">:</TextBlock>
                        <TextBlock Foreground="Red" FontSize="20" Text="{Binding Sec, FallbackValue=00, StringFormat=00}" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                    </StackPanel>
                    <!-- Creates the effect of turned of segments -->
                    <StackPanel Orientation="Horizontal" Margin="2">
                        <TextBlock Opacity="0.1" FontSize="20" Text="88" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Opacity="0.1" FontSize="20" FontFamily="DSEG7 Classic Mini" >:</TextBlock>
                        <TextBlock Opacity="0.1" FontSize="20" Text="88" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                        <TextBlock Opacity="0.1" FontSize="20" FontFamily="DSEG7 Classic Mini">:</TextBlock>
                        <TextBlock Opacity="0.1" FontSize="20" Text="88" FontFamily="DSEG7 Classic Mini" ></TextBlock>
                    </StackPanel>
                </Grid>
            </Border>

enter image description here

it would be even easier using only one textblock and a string format but this example I already had lying around :)

Denis Schaf
  • 2,478
  • 1
  • 8
  • 17