An elegant way to implement this IMHO is to use a container for dependency injection, which will provide greater flexibility, allowing you to completely separate problems (for example, implementing settings from view models and user controls).
There are many DI frameworks there, for my example I will use a simple injector because it is free (open source), simple, but you can apply the same principle to other frameworks (Unity, Ninject, etc.).
Start by creating an interface for the settings service, for example:
public interface ISettingsService { double Volumne { get; set; } string LastMediaUrl { get; set; } MediaStatus PlayingMediaStatus; void SaveSettings(); }
Then add your implementation for the service, the beauty of using DI is that you can change the implementation at any time or completely replace it, and your application will continue to work as usual.
Suppose you want to use the application settings, here is your service:
public class SettingsServiceFromApplication : ISettingsService { public double Volume { get { return Properties.Settings.Volume; } } [...] }
Or say you want to use the database to store your settings:
public class SettingsServiceFromDb : ISettingsService { public double Volume { get { return MyDb.Volumen; } } [...] }
Then you can use the DI container to indicate which implementation to use:
Start by installing the library using NuGet:
Install-Package SimpleInjector -Version 4.0.12
You need a way to share your container throughout the application, usually I just run a static class that I initialize when the application starts:
using Container = SimpleInjector.Container; namespace YourNamespace { public class Bootstrapper { internal static Container Container; public static void Setup() {
You need to call the installer when the application starts, the best place in the application constructor:
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); Bootstrapper.Setup(); } }
So now you have an injection container with an application extension that you can use to query for βservicesβ (specific implementations of the interface).
To get the implementation of settings in view models, you can simply call the container as follows:
To simplify the work, I usually create a basic presentation model that will make it easier to get services, for example:
public abstract BaseViewModel { private ISettingsService _settings; protected ISettingsService GeneralSettings { get { if (_settings == null) _settings = Bootstrapper.Container.GetInstance<ISettingsService>(); return _settings; } } }
Each view model inheriting from this class will have access to the settings:
public class YourViewModel : BaseViewModel { private void OnMediaEndedCommand() { if (GeneralSettings.PauseOnLastFrame) { MediaPlayer.SetMediaState(MediaPlayerStates.Pause); return; } if (PlayListViewModel.FilesCollection.Last().Equals(PlayListViewModel.FilesCollection.Current) && !Repeat) { ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext())); MediaPlayer.SetMediaState(MediaPlayerStates.Stop); return; } ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext())); } }
As you can see, the code is the same as your code! But now the settings come from your container. Where is the elegance? Well, tell me that in a year someone will decide that you will save your settings in the database, what do you need to change in your code?
Container.Register<ISettingsService, SettingsServiceFromDb>();
One line. Everything else should work as usual.
Like view models, you can use this mechanism in your own control:
public class MyMediaElement : UserControl //Or MediaElement and instead of commands you can override real events in the control code behind, this does not break the MVVM pattern at all, just make sure you use depedency properties if you need to exchange data with your view models { private void OnMediaEndedCommand() { //Get your settings from your container, do whatever you want to do depending on the settings [...] } }
Then just use your control in your views / ViewModels:
<local:MyMediaElement />
Yes, all you need is because you process everything in your user / user control, you do not need to care about how you handle the settings in the control.
There are many options that you can use to register containers, I recommend that you take a look at the documents:
https://simpleinjector.org/index.html https://simpleinjector.readthedocs.io/en/latest/index.html