Reputation: 377
I am doing a program, which must have a clock hanging every second, main problem is that I am beginner in WPF and MVVM :)
But otherwise my clock is running just not refreshing. I have special class for only Time and Date purpose.
Here is my code:
TimeDate class:
public class TimeDate : ViewModelBase
{
public string SystemTimeHours;
string SystemTimeLong;
string SystemDateLong;
string SystemTimeZoneLong;
string SystemTimeShort;
string SystemDateShort;
string SystemTimeZoneShort;
public void InitializeTimer()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
SystemTimeHours = DateTime.Now.ToString("HH:mm tt");
}
}
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
TimeDate td = new TimeDate();
td.InitializeTimer();
HoursTextBox = td.SystemTimeHours;
}
private string _hourstextbox;
public string HoursTextBox
{
get
{ return _hourstextbox; }
set
{
if (value != _hourstextbox)
{
_hourstextbox = value;
NotifyPropertyChanged("HoursTextBox");
}
}
}
}
And also NotifyProperty in ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected virtual void NotifyPropertyChangedAll(object inOjbect)
{
foreach (PropertyInfo pi in inOjbect.GetType().GetProperties())
{
NotifyPropertyChanged(pi.Name);
}
}
public virtual void Refresh()
{
NotifyPropertyChangedAll(this);
}
#endregion // INotifyPropertyChanged Members
#region IDisposable Members
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
/// </summary>
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
#endregion // IDisposable Members
}
What to do, that Clock will refresh every second? Please help
Upvotes: 7
Views: 9957
Reputation: 7908
An example of a static wrapper for a clock and binding to it:
using System.Threading;
public static class ClockForWpf
{
private static readonly Timer timer = new Timer(Tick, null, 0, 10);
private static void Tick(object state)
{
Time = DateTime.Now;
TimeChanged?.Invoke(null, EventArgs.Empty);
}
public static event EventHandler TimeChanged;
public static DateTime Time { get; private set; }
}
<TextBlock Text="{Binding Path=(local:ClockForWpf.Time)}"/>
Upvotes: 1
Reputation: 27338
You have to specify the DispatcherTimer as a field in your viewmodel class, not just as a local variable in ctor.
Garbage Collector will destroy all local variables when they are out of scope.
Here is my implementation of the clock :)
public class MainWindowViewModel : ViewModelBase
{
private readonly DispatcherTimer _timer;
public MainWindowViewModel()
{
_timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(1)};
_timer.Start();
_timer.Tick += (o, e) => OnPropertyChanged("CurrentTime");
}
public DateTime CurrentTime { get { return DateTime.Now; } }
}
<TextBlock Text="{Binding CurrentTime, StringFormat={}{0:HH:mm tt}}" />
Upvotes: 5
Reputation: 2141
As MSDN:
Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.
I make shorter example:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="98" Width="128"
xmlns:local="clr-namespace:WpfApplication1">
<Window.DataContext>
<!-- This will auto create an instance of ViewModel -->
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Name="lblTimer" Grid.Row="1" Content="{Binding Path=CurrentTime}"></Label>
</Grid>
</Window>
CS:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ViewModel : ViewModelBase
{
private string _currentTime;
public DispatcherTimer _timer;
public string CurrentTime
{
get
{
return this._currentTime;
}
set
{
if (_currentTime == value)
return;
_currentTime = value;
OnPropertyChanged("CurrentTime");
}
}
public ViewModel()
{
_timer = new DispatcherTimer(DispatcherPriority.Render);
_timer.Interval = TimeSpan.FromSeconds(1);
_timer.Tick += (sender, args) =>
{
CurrentTime = DateTime.Now.ToLongTimeString();
};
_timer.Start();
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Hope this help.
Upvotes: 6