> For the complete documentation index, see [llms.txt](https://elguerre.gitbook.io/de0an/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://elguerre.gitbook.io/de0an/capitulo-5-la-construccion.-implementando-requisitos/configuracion-settings.md).

# Pantalla de Configuración (o Settings)

La página de configuración del usuario que conocemos como *Config o Settings*, pretende ofrecer al usuario una personalización mucho más cercana a sus gustos, de manera que él sea quien decida los valores de ciertos parámetros de la aplicación.

Un aspecto diferenciador de esta página frente al resto, es que los valores para los parámetros que ofrezcamos al usuario se guardan en el disco (o *storage*) del dispositivo (en una zona aislada y reservada para cada aplicación) persistiendo entre una sesión y otra.

Comenzaremos creando la página básica de personalización, según la tenemos reflejada en nuestros requisitos.

![](https://240651724-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfrvHBNivOTy0fVKROgzo%2Fuploads%2Fgit-blob-d6d2907fc0b04e6853fb4d6b5fb1eb2f8de509d0%2Fimage14.png?alt=media)

**Figura 14.- Pantalla “Configuración (o Settings)” según la maqueta**

## Creando la página

Como ya tenemos una página de configuración predeterminada creada automáticamente por la plantilla Template 10, partiremos de ella eliminando todo aquello que no necesitamos:

* Modificar el nombre del ViewModel *SettingsPageViewModel* para que, tras aplicar nuestra nomenclatura, su nuevo nombre sea *SettingsViewModel*.
* Modificar *Shell.xaml.cs*, para eliminar las entradas siguientes, dado que no las utilizaremos.

```csharp
HamburgerMenu.IsFullScreen = _settings.IsFullScreen;
HamburgerMenu.HamburgerButtonVisibility =
    _settings.ShowHamburgerButton ?
    Visibility.Visible : Visibility.Collapse
```

* Modificar la clase *App.xaml.cs* para eliminar las siguientes entradas, estableciendo así los valores predeterminados.

```csharp
CacheMaxDuration = settings.CacheMaxDuration;
ShowShellBackButton = settings.UseShellBackButton;
```

* Mover el servicio *SettingService* a la carpeta raíz donde se encuentran el resto de servicios y eliminar la carpeta *SettingsServices*.

Tras estos cambios, actualizaremos la clase *SettingsViewModel*, añadiendo cada una de las propiedades públicas que conformarán los parámetros de configuración definidos en su interfaz *ISettingsViewModel*.

```csharp
private ISettingsService settings;
public SettingsViewModel(ISettingsService settings)
{
    this.settings = settings;
}
public bool UseLightTheme
{
    get { return settings.AppTheme.Equals(ApplicationTheme.Light); }
    set { settings.AppTheme = value ? ApplicationTheme.Light : ApplicationTheme.Dark; base.RaisePropertyChanged(); }
}
public double PomodoroWorkingTime
{
    get { return settings.PomodoroWorkingTime; }
    set { settings.PomodoroWorkingTime = value; base.RaisePropertyChanged(); }
}
...
```

En este código podemos ver un nuevo servicio *SettingServices* y su interfaz *ISettingsServices*. Lo utilizaremos para la obtención y guardado de los valores de los parámetros en disco (o *storage*). El SDK nos facilita esta labor gracias a la clase *Windows.Storage.ApplicationData* y, más concretamente a su propiedad *Current*, que nos da acceso al almacenamiento de datos asociado a la aplicación.

Una vez más, con Template 10 minimizaremos el esfuerzo de desarrollo usando la clase *Template10.Services.SettingsService.SettingsHelper*, que proporciona la lectura y escritura a través de sus métodos, *Load* y *Write*.

```csharp
...
public SettingsService()
{
    helper = new Template10.Services.SettingsService.SettingsHelper();
}
public static ISettingsService Instance
    => ViewModelLocator.Current.GetInstance<ISettingsService>();
public ApplicationTheme AppTheme
{
    get
    {
        var theme = ApplicationTheme.Light;
        var value = helper.Read<string>(nameof(AppTheme), theme.ToString());
        return Enum.TryParse<ApplicationTheme>(value, out theme) ? theme : ApplicationTheme.Dark;
    }
    set
    {
        helper.Write(nameof(AppTheme), value.ToString());
        (Window.Current.Content as FrameworkElement).RequestedTheme = value.ToElementTheme();
        Views.Shell.HamburgerMenu.RefreshStyles(value, true);
    }
}
public double PomodoroWorkingTime
{
    get
    {
        var workingTime = Load<uint>(nameof(PomodoroWorkingTime));
        if (workingTime <= 0)
            return defaultPomodoroWorkingTimeInMin;
        return workingTime;
    }
    set { Save(nameof(PomodoroWorkingTime), (uint)value); }
}
…
```

En el código podemos ver cómo creamos la instancia de la clase *SettingsHelper* que a posteriori utilizamos para las lecturas y escrituras de parámetros.

La propiedad *Instance*, retorna una instancia del propio servicio *SettingsService* que facilita el acceso al mismo desde las clases *Shell.xaml.cs* y *App.xaml.cs*. Se trata de la única instancia del servicio, dado que el contenedor de inyección de dependencias es SimpleIoC. El cual, por defecto, retorna siempre las instancias registradas como instancias únicas haciendo así, las veces de *Singleton*[¹](/de0an/capitulo-5-la-construccion.-implementando-requisitos/que-hemos-aprendido.md#notas).

El resto de propiedades hacen referencia a los parámetros de usuario, de las cuales, cabe destacar el *Set* de la propiedad *AppTheme*, donde se realiza la asignación del Tema elegido por el usuario, que, a su vez, se refleja en toda la aplicación.

Completados estos puntos y creadas las interfaces *ISettingsViewModel*, *ISettingsService* y la clase *SettingsViewmodelMock*, sólo nos queda hacer los registros en el contenedor de ID.

```csharp
static ViewModelLocator()
{
    ...
    SimpleIoc.Default.Register<ISettingsService, SettingsService>();
    if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
    {
        SimpleIoc.Default.Register<IAboutViewModel, AboutViewModelMock>();
        SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModelMock>();
    }
    else
    {
        SimpleIoc.Default.Register<IAboutViewModel, AboutViewModel>();
        SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModel>();
    }
    ...
}
...
public ISettingsViewModel Settings { get { return current.GetInstance<ISettingsViewModel>(); } }
```

En cuanto a la **página&#x20;*****SettingsPage.xaml*** la crearemos tal y como podemos ver en la siguiente figura, que ilustra tres vistas durante su fase de diseño: vista previa, código XAML y ventana de propiedades, para uno de los controles *Slider*.

![](https://240651724-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfrvHBNivOTy0fVKROgzo%2Fuploads%2Fgit-blob-ae45a32b2a7da2a56590c639b1a7e7e56f88ec7b%2Fimage15.png?alt=media)

**Figura 15.- Página de configuración en fase de diseño**

Para que la vista en modo diseño tenga los valores deseados, debemos haber desarrollado el *Mock* implementando las propiedades de la interfaz *ISettingsViewModel*.

```csharp
public class SettingsViewModelMock : ViewModelBase, ISettingsViewModel
{
    public RelayCommand AppBarResetCommand { get; }
    public bool UseLightTheme { get; set; }
    public double PomodoroWorkingTime
    { get => 25; set { value = 25; } }
    public double PomodoroShortBreakTime
    { get => 5; set => value = 5; }
    public double PomodoroLongBreakTime
    { get => 15; set => value = 15; }
}
```

Para el enlazado a datos (o *Binding*) de los controles *Slider*, hemos usado el modo *TwoWay*, que es totalmente necesario en estos casos ya que el usuario interactuará directamente con ellos transmitiéndose los cambios desde la vista al ViewModel y, desde el ViewModel a la vista para poder establecer sus valores predeterminados o personalizados.

## Opciones de menú

Las opciones de menú se resumen en una sola. Restituir los valores modificados por el usuario a sus valores originales. Denominaremos a esta acción reiniciar/reestablecer (o *reset*) y la implementaremos incluyendo un método en el servicio *SettingsService* y un comando en su ViewModel.

**Servicio**:

```csharp
public void Reset()
{
    PomodoroWorkingTime = defaultPomodoroWorkingTimeInMin;
    PomodoroShortBreakTime = defaultPomodoroShortBeakTimeInMin;
    PomodoroLongBreakTime = defaultPomodoroLongBeakTimeInMin;
}
```

**ViewModel**:

```csharp
private RelayCommand appBarResetCommand;
public RelayCommand AppBarResetCommand
{
    get
    {
        return appBarResetCommand
            ?? (appBarResetCommand = new RelayCommand(
                () =>
        {
            settings.Reset();
            RaisePropertyChanged(nameof(PomodoroWorkingTime));
            RaisePropertyChanged(nameof(PomodoroShortBreakTime));
            RaisePropertyChanged(nameof(PomodoroLongBreakTime));
        }));
    }
}
```

La siguiente figura muestra la ejecución de la aplicación en un PC a la izquierda con el idioma configurado en español, y, en un teléfono móvil a la derecha, con el idioma configurado en inglés. Así mismo, en el PC se ha marcado el Tema/Modo Claro mientras que, en el teléfono, Oscuro.

![](https://240651724-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfrvHBNivOTy0fVKROgzo%2Fuploads%2Fgit-blob-e1fbd6f84f95f7992cd1f7182bb3bc8cc27bc4b1%2Fimage16.png?alt=media)

**Figura 16.- Ejecución de la página de configuración en PC y teléfono**

{% hint style="info" %}
**Nota**: Es probable que la página de configuración vaya creciendo en funcionalidad e incluyendo nuevos parámetros a medida que continuamos desarrollando la aplicación. En cualquier caso, no olvidemos ir recogiendo cada nueva funcionalidad como parte de los requisitos.
{% endhint %}
