-1

I want to know how to display current date and time on a WPF statusbar.

I know this is too basic a question, but I am new to .net WPF, and I know this can be done in a Form application easily.

Thanks in advance.

Ting

Chengting
  • 345
  • 2
  • 4
  • 11
  • Please, show us what you've tried already – Pavel Anikhouski May 22 '20 at 07:25
  • instead of trying anything, i have been looking for some similar control available in Form application's toolbox. I am sure I can manage to display the date and time anyway, but am afraid that will not be the common practice, due to lack of knowledge of WPF. I had thought, a ready-to-use control will be there to be used, instead of making it by myself. So the question is really is not how to display a time or date, but in what way. And should I give up my Form application's thinking and just do this kind of work by myself code? – Chengting May 22 '20 at 12:09

2 Answers2

2

Create a wpf project. In your MainWindow.xaml add the StatusBar and handle Loaded event of window.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="WpfApp1.MainWindow"
        Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StatusBar Grid.Row="1">
            <TextBlock x:FieldModifier="private" x:Name="myDateTime"/>
        </StatusBar>
    </Grid>
</Window>

In the MainWindow.xaml.cs add the following namespaces (if they are not exist):

using System;
using System.Windows;
using System.Windows.Threading;

And in the Loaded enevt handler you can use DispatcherTimer for update textblock text property every second:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
   DispatcherTimer timer = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, (object s, EventArgs ev) =>
   {
      this.myDateTime.Text = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss");
   }, this.Dispatcher);
   timer.Start();
}

Also there are a lot of examples for customize wpf controls using Template and Style properties. just search for it.

and many more.

Also you can implement your custom WPFTimer. This simple implementation get you an idea for do this.

public class WPFTimer : TextBlock
{
   #region static

   public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(TimeSpan), typeof(WPFTimer), new PropertyMetadata(TimeSpan.FromSeconds(1), IntervalChangedCallback));
   public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register("IsRunning", typeof(bool), typeof(WPFTimer), new PropertyMetadata(false, IsRunningChangedCallback));

   private static void IntervalChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
      WPFTimer wpfTimer = (WPFTimer)d;
      wpfTimer.timer.Interval = (TimeSpan)e.NewValue;
   }

   private static void IsRunningChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
      WPFTimer wpfTimer = (WPFTimer)d;
      wpfTimer.timer.IsEnabled = (bool)e.NewValue;
   }

   #endregion

   private readonly DispatcherTimer timer;

   [Category("Common")]
   public TimeSpan Interval
   {
      get
      {
         return (TimeSpan)this.GetValue(IntervalProperty);
      }
      set
      {
         this.SetValue(IntervalProperty, value);
      }
   }

   [Category("Common")]
   public bool IsRunning
   {
      get
      {
         return (bool)this.GetValue(IsRunningProperty);
      }
      set
      {
         this.SetValue(IsRunningProperty, value);
      }
   }

   public WPFTimer()
   {
      this.timer = new DispatcherTimer(this.Interval, DispatcherPriority.Normal,this.Timer_Tick ,this.Dispatcher);
      this.timer.IsEnabled = false;
   }

   private void Timer_Tick(object sender, EventArgs e)
   {
      this.SetValue(TextProperty, DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
   }
}

And now you have a control that can use it in the designer.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        x:Class="WpfApp1.MainWindow"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StatusBar Grid.Row="1">
            <local:WPFTimer IsRunning="True"/>
        </StatusBar>
    </Grid>
</Window>
  • many thanks! I ever thought of trying this way, but imagined there would a way like what I did in a Form application: just put a control (item) on it. For WPF, really, I have to do such kind of work to display a time? why dont display it automatically for me, I thought that is the purpose of using a control. – Chengting May 22 '20 at 04:23
  • @Chengting Your welcome. But this is not a hard work. wpf has a rich designer that do more of this works visually. But if you mean a control like windows forms timer? [in wpf usual time is the `DispatcherTimer`](https://stackoverflow.com/a/5410783/5758877). – Hamid Reza Mohammadi May 22 '20 at 04:39
  • @Chengting Also you can implement your custom `WPFTimer`. see my edit. – Hamid Reza Mohammadi May 22 '20 at 05:35
  • yes,the WPFTimer is what I expected. I took it for granted that there is such a control ready to use there in toolbox. – Chengting May 22 '20 at 11:59
2

You can do it this way, using wpf animation and binding, and no background code :)

<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>

or like this,a real clock

<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">
    <Window.Resources>
        <FrameworkElement x:Key="time" Tag="{x:Static system: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>

Create a clock using only xaml code in wpf

ZiYuCai1984
  • 46
  • 1
  • 3