Here is the program structure, App > Window > DataContext binding to MainViewModel
The MainViewModel contains 3 sub-ViewModel property, named as
- ViewModelA
- ViewModelB
- ViewModelC
each of sub-viewModel contains a dispatchertimer to get data from API and display on WPF.
I am going to inject a common voice service into all sub-viewModel, that will used by sub-viewModel to play voice.
The voiceService designed as singleton and allows sub-viewModel to add data, voiceService running a timer that watches on a List, play voice when new data arrived.
But I found the unity container fires the service twice, did my setting is incorrect
// MainWindow.xaml.cs
VoiceService voiceService = new VoiceService();
AutoMapperService autoMapperService = new AutoMapperService();
this.DataContext = new MainViewModel(voiceService, autoMapperService);
//this.DataContext = _container.Resolve<IMainViewModel>();
// app.xaml.cs
private void InitContainer(IUnityContainer container)
{
container.RegisterType<IMainViewModel, MainViewModel>();
container.RegisterType<IVoiceService, VoiceService>();
container.RegisterType<IAutoMapperService, AutoMapperService>();
// for Unity 5, Unity has been broken down into a set of Packages for you to install only what you need.
// If you need to do Registration By Convention -
// which is what the RegisterTypes() method is used for,
// you need to also install the NuGet Package,
// Unity.RegistrationByConvention(version 2.1.0 as of this writing).
// With Unity >=3, you can skip the container registration if you follow the naming convention like this:
// This will register all types with a ISample/Sample naming convention
//container.RegisterTypes(
// AllClasses.FromLoadedAssemblies(),
// WithMappings.FromMatchingInterface,
// WithName.Default);
}
public class VoiceService : IVoiceService, IDisposable
{
protected readonly System.Timers.Timer announceTimer;
protected readonly int defaultDataTimeInterval = 10;
private List<VoiceAnnouncement> announcementQueueList = new List<VoiceAnnouncement>();
private List<VoiceAnnouncement> announcedRecordList = new List<VoiceAnnouncement>();
public VoiceAnnouncement currentAnnouncement = new VoiceAnnouncement();
private SpeechSynthesizer speaker = new SpeechSynthesizer();
private volatile bool _isCurrentlySpeaking = false;
/// <summary>Event handler. Fired when the SpeechSynthesizer object starts speaking asynchronously.</summary>
private void StartedSpeaking(object sender, SpeakStartedEventArgs e)
{ this.GetInstance()._isCurrentlySpeaking = true; }
/// <summary>Event handler. Fired when the SpeechSynthesizer object finishes speaking asynchronously.</summary>
private void FinishedSpeaking(object sender, SpeakCompletedEventArgs e)
{ this.GetInstance()._isCurrentlySpeaking = false; }
public event EventHandler OnSomeEvent = delegate { };
public bool IsCurrentlySpeaking
{
get { return this._isCurrentlySpeaking; }
}
private static readonly Lazy<VoiceService> lazyAnnounceService = new Lazy<VoiceService>(() => new VoiceService());
public VoiceService()
{
// don't use dispatchertime, use timer
// otherwise the screen will freeze when speak
this.announceTimer = new System.Timers.Timer(TimeSpan.FromSeconds(this.defaultDataTimeInterval).TotalMilliseconds);
this.announceTimer = new System.Timers.Timer(5000);
announceTimer.Elapsed += PlayVoiceTimerTickTest;
announceTimer.Start();
//this.announceTimer = new DispatcherTimer();
//announceTimer.Interval = TimeSpan.FromSeconds(this.defaultDataTimeInterval);
//announceTimer.Tick += PlayVoiceTimerTick;
//announceTimer.Start();
this.speaker.SpeakStarted += new EventHandler<SpeakStartedEventArgs>(StartedSpeaking);
this.speaker.SpeakCompleted += new EventHandler<SpeakCompletedEventArgs>(FinishedSpeaking);
}
public VoiceService GetInstance()
{
return lazyAnnounceService.Value;
}
void IDisposable.Dispose()
{
this.announceTimer.Elapsed -= this.PlayVoiceTimerTickTest;
this.speaker.Dispose();
}