> 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-9-la-construccion.-navegacion-y-configuracion-de-usuario/lock-screen.md).

# Lock Screen

La **pantalla de bloqueo (Lock-Screen)** es una de las primeras interacciones que un usuario tiene con una aplicación en un dispositivo Windows. Personalizar la pantalla de bloqueo no solo mejora la estética de la aplicación, sino que también puede proporcionar información útil y accesos directos a funcionalidades clave. En esta sección, aprenderemos cómo establecer una imagen personalizada en la pantalla de bloqueo utilizando el servicio `LockScreenService.cs`. Además, veremos cómo configurar las declaraciones necesarias en el archivo `Package.appxmanifest` para permitir esta funcionalidad.

## Configuración de Declaraciones en el Archivo de Manifiesto

Antes de poder modificar la pantalla de bloqueo, es esencial declarar las capacidades necesarias en el archivo de manifiesto de la aplicación (`Package.appxmanifest`). Esto asegura que la aplicación tenga los permisos adecuados para realizar cambios en la configuración del sistema.

### Paso 1: Abrir el Archivo `Package.appxmanifest`

1. En el **Explorador de Soluciones** de Visual Studio, haz doble clic en el archivo `Package.appxmanifest` para abrirlo en el editor de manifiestos.
2. Alternativamente, puedes hacer clic derecho sobre el archivo y seleccionar **Abrir con > Editor XML** si prefieres editar el XML directamente.

### Paso 2: Agregar las Declaraciones Necesarias

Dentro del archivo de manifiesto, agrega las siguientes declaraciones dentro de la sección `<Capabilities>` para permitir que la aplicación modifique la pantalla de bloqueo:

```xml
<Capabilities>
    <!-- Otras capacidades existentes -->
    <uap:Capability Name="userAccountInformation" />
    <rescap:Capability Name="userDataTasks" />
</Capabilities>
```

**Explicación de las Capacidades:**

* `<uap:Capability Name="userAccountInformation" />`: Permite que la aplicación acceda a información de la cuenta de usuario.
* `<rescap:Capability Name="userDataTasks" />`: Permite que la aplicación realice tareas relacionadas con los datos del usuario, incluyendo la personalización de la pantalla de bloqueo.

> **Nota:** Asegúrate de declarar el espacio de nombres `rescap` en la raíz del archivo de manifiesto si aún no está presente:

```xml
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
```

## Implementación del Servicio `LockScreenService.cs`

El servicio `LockScreenService` es responsable de cambiar la imagen de fondo de la pantalla de bloqueo. A continuación, revisaremos el código proporcionado y explicaremos cómo funciona.

### Código del Servicio `LockScreenService.cs`

```csharp
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.System.UserProfile;

public static class LockScreenService
{
    /// <summary>
    /// Cambia la imagen de fondo de la pantalla de bloqueo.
    /// </summary>
    /// <param name="imagePath">Ruta de la imagen que se establecerá como fondo.</param>
    /// <returns>True si la operación fue exitosa; de lo contrario, false.</returns>
    public static async Task<bool> ChangeLockScreenBackground(string imagePath)
    {
        bool success = false;
        if (UserProfilePersonalizationSettings.IsSupported())
        {
            try
            {
                // Crear una URI a partir de la ruta de la imagen.
                // Asegúrate de que la imagen esté incluida en los assets de la aplicación.
                Uri uri = new Uri(imagePath);

                // Obtener el archivo de almacenamiento desde la URI.
                StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);

                // Obtener las configuraciones de personalización del perfil de usuario.
                UserProfilePersonalizationSettings settings = UserProfilePersonalizationSettings.Current;

                // Intentar establecer la imagen de la pantalla de bloqueo.
                success = await settings.TrySetLockScreenImageAsync(file);
            }
            catch (Exception ex)
            {
                // Manejar excepciones, como archivo no encontrado o permisos insuficientes.
                // Puedes registrar el error utilizando el gestor de trazas.
                TraceManager.Log($"Error al cambiar la imagen de la pantalla de bloqueo: {ex.Message}");
            }
        }
        else
        {
            TraceManager.Log("La personalización de la pantalla de bloqueo no está soportada en este dispositivo.");
        }
        return success;
    }
}
```

**Explicación del Código:**

1. **Verificación de Soporte:**
   * `UserProfilePersonalizationSettings.IsSupported()`: Comprueba si el dispositivo actual soporta la personalización de la pantalla de bloqueo.
2. **Creación de la URI y Obtención del Archivo:**
   * Se crea una instancia de `Uri` a partir de la ruta proporcionada (`imagePath`).
   * `StorageFile.GetFileFromApplicationUriAsync(uri)`: Obtiene el archivo de imagen desde la URI especificada. Asegúrate de que la imagen esté incluida en los assets de la aplicación y su ruta sea correcta (por ejemplo, `ms-appx:///Assets/LockScreenImage.jpg`).
3. **Establecimiento de la Imagen de la Pantalla de Bloqueo:**
   * `UserProfilePersonalizationSettings.Current.TrySetLockScreenImageAsync(file)`: Intenta establecer la imagen proporcionada como fondo de la pantalla de bloqueo. Devuelve `true` si la operación es exitosa.
4. **Manejo de Excepciones:**
   * Captura cualquier excepción que pueda ocurrir durante el proceso y la registra utilizando el `TraceManager`.

### Uso del Servicio en el ViewModel

Para utilizar el servicio `LockScreenService`, puedes llamarlo desde un ViewModel cuando el usuario realice una acción específica, como seleccionar una nueva imagen de fondo.

```csharp
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Windows.UI.Popups;

public class SettingsViewModel : INotifyPropertyChanged
{
    // Propiedades y comandos del ViewModel

    public ICommand ChangeLockScreenCommand { get; }

    public SettingsViewModel()
    {
        ChangeLockScreenCommand = new RelayCommand(async () => await ChangeLockScreen());
    }

    private async Task ChangeLockScreen()
    {
        string imagePath = "ms-appx:///Assets/LockScreenImage.jpg"; // Ruta de la imagen

        bool result = await LockScreenService.ChangeLockScreenBackground(imagePath);

        if (result)
        {
            var dialog = new MessageDialog("La pantalla de bloqueo se ha actualizado correctamente.");
            await dialog.ShowAsync();
        }
        else
        {
            var dialog = new MessageDialog("No se pudo actualizar la pantalla de bloqueo.");
            await dialog.ShowAsync();
        }
    }

    // Implementación de INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
```

**Explicación del Código:**

* **Comando `ChangeLockScreenCommand`:**
  * Cuando se ejecuta este comando (por ejemplo, al hacer clic en un botón en la interfaz de usuario), se llama al método `ChangeLockScreen`.
* **Método `ChangeLockScreen`:**
  * Define la ruta de la imagen que se establecerá como fondo de la pantalla de bloqueo.
  * Llama al servicio `LockScreenService.ChangeLockScreenBackground(imagePath)` para intentar cambiar la imagen.
  * Muestra un mensaje de éxito o error al usuario basado en el resultado de la operación.

### Integración en la Vista (XAML)

A continuación, se muestra cómo vincular el comando `ChangeLockScreenCommand` a un botón en la interfaz de usuario para permitir a los usuarios cambiar la imagen de la pantalla de bloqueo.

```xml
<Page
    x:Class="MyApp.SettingsPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp"
    xmlns:vm="using:MyApp.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.DataContext>
        <vm:SettingsViewModel x:Name="ViewModel"/>
    </Page.DataContext>

    <Grid Padding="20">
        <StackPanel Spacing="20">
            <!-- Otros controles de configuración -->

            <!-- Botón para Cambiar la Imagen de la Pantalla de Bloqueo -->
            <Button Content="Cambiar Fondo de Pantalla de Bloqueo"
                    Command="{Binding ChangeLockScreenCommand}"
                    Width="250"
                    Height="50"/>
        </StackPanel>
    </Grid>
</Page>
```

**Explicación del Código:**

* **Botón `Cambiar Fondo de Pantalla de Bloqueo`:**
  * Al hacer clic en este botón, se ejecuta el comando `ChangeLockScreenCommand` del ViewModel, lo que desencadena el proceso de cambio de la imagen de la pantalla de bloqueo.

## Apertura desde una Cita/Eventos

Además de permitir la personalización de la pantalla de bloqueo, es posible que desees que tu aplicación se abra directamente desde una cita o evento específico. Esto puede ser útil para aplicaciones de calendario, gestión de tareas o cualquier aplicación que maneje eventos.

### Implementación de la Apertura desde una Cita/Eventos

1. **Definir un Protocolo Personalizado en el Manifiesto:**
   * Como se explicó en la sección anterior, define un protocolo personalizado en el archivo `Package.appxmanifest`. Por ejemplo, `myapp://appointment?id=123`.
2. **Manejar la Activación desde el Protocolo:**

   * En el archivo `App.xaml.cs`, implementa la lógica para manejar la activación desde el protocolo y navegar a la página de detalle correspondiente.

   ```csharp
   // App.xaml.cs
   using Windows.ApplicationModel.Activation;
   using Windows.UI.Xaml;
   using Windows.UI.Xaml.Controls;

   sealed partial class App : Application
   {
       public static IServiceProvider Services { get; private set; }

       public App()
       {
           this.InitializeComponent();
           this.Suspending += OnSuspending;
           ConfigureServices();
       }

       private void ConfigureServices()
       {
           var serviceCollection = new ServiceCollection();

           // Registro de proveedores
           serviceCollection.AddSingleton<ICalendarProvider, CalendarProvider>();
           serviceCollection.AddSingleton<IProjectRepository, ProjectRepository>();

           // Registro de servicios
           serviceCollection.AddSingleton<AppointmentService>();
           serviceCollection.AddSingleton<ProjectHelper>();

           // Registro de ViewModels y otros servicios según sea necesario
           // serviceCollection.AddTransient<MainViewModel>();
           // serviceCollection.AddTransient<SettingsViewModel>();
           // ...

           Services = serviceCollection.BuildServiceProvider();
       }

       protected override async void OnLaunched(LaunchActivatedEventArgs e)
       {
           Frame rootFrame = Window.Current.Content as Frame;

           if (rootFrame == null)
           {
               rootFrame = new Frame();
               rootFrame.NavigationFailed += OnNavigationFailed;

               Window.Current.Content = rootFrame;
           }

           if (e.PrelaunchActivated == false)
           {
               if (rootFrame.Content == null)
               {
                   rootFrame.Navigate(typeof(MainPage), e.Arguments);
               }
               Window.Current.Activate();
           }

           // Manejo de apertura desde una cita/evento
           HandleActivation(e);
       }

       private async void HandleActivation(IActivatedEventArgs args)
       {
           if (args.Kind == ActivationKind.Protocol)
           {
               var protocolArgs = args as ProtocolActivatedEventArgs;
               string uri = protocolArgs.Uri.ToString();

               // Parsear la URI para determinar la acción
               // Ejemplo de URI: myapp://appointment?id=123
               var uriScheme = new Uri(uri);
               if (uriScheme.Host.Equals("appointment", StringComparison.OrdinalIgnoreCase))
               {
                   var queryParams = Microsoft.Toolkit.Uwp.Helpers.SystemNavigationHelper.ParseQueryString(uriScheme.Query);
                   if (queryParams.ContainsKey("id") && int.TryParse(queryParams["id"], out int appointmentId))
                   {
                       var frame = Window.Current.Content as Frame;
                       frame.Navigate(typeof(AppointmentDetailPage), appointmentId);
                   }
               }
           }
       }

       private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
       {
           throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
       }

       private void OnSuspending(object sender, SuspendingEventArgs e)
       {
           // Guardar el estado de la aplicación
       }
   }
   ```
3. **Crear la Página de Detalle de la Cita/Eventos:**

   * Implementa una página (`AppointmentDetailPage`) que muestre los detalles de la cita o evento basado en el `appointmentId` recibido.

   ```csharp
   // AppointmentDetailPage.xaml.cs
   using Windows.UI.Popups;

   public sealed partial class AppointmentDetailPage : Page
   {
       private readonly AppointmentService _appointmentService;
       private int _appointmentId;

       public AppointmentDetailPage()
       {
           this.InitializeComponent();
           _appointmentService = App.Services.GetService<AppointmentService>();
       }

       protected override async void OnNavigatedTo(NavigationEventArgs e)
       {
           if (e.Parameter is int appointmentId)
           {
               _appointmentId = appointmentId;
               var appointment = await _appointmentService.GetAppointmentsAsync(DateTime.Now)
                                                        .ContinueWith(t => t.Result.FirstOrDefault(a => a.Id == _appointmentId));

               if (appointment != null)
               {
                   // Asignar los detalles de la cita al ViewModel o controles de la UI
                   AppointmentNameTextBlock.Text = appointment.Name;
                   AppointmentDateTextBlock.Text = appointment.Date.ToString("f");
                   // ...
               }
               else
               {
                   // Manejar el caso donde la cita no se encuentra
                   var dialog = new MessageDialog("La cita no fue encontrada.");
                   await dialog.ShowAsync();
                   Frame.GoBack();
               }
           }
       }
   }
   ```
4. **Probar la Apertura desde una Cita/Eventos:**

   * Crea una URI personalizada que siga el esquema definido y ejecútala para verificar que la aplicación navegue correctamente a la página de detalle.

   **Ejemplo de URI:**

   ```
   myapp://appointment?id=123
   ```

   * Al ejecutar esta URI (por ejemplo, ingresándola en el navegador o mediante una aplicación externa), Windows iniciará la aplicación y navegará automáticamente a `AppointmentDetailPage`, mostrando los detalles de la cita con `id=123`.

## Consideraciones Finales

* **Validación de Permisos:** Asegúrate de que la aplicación solicite y obtenga los permisos necesarios para modificar la pantalla de bloqueo. Sin los permisos adecuados, la operación fallará.
* **Gestión de Errores:** Implementa un manejo robusto de errores para informar al usuario en caso de que la personalización de la pantalla de bloqueo no sea posible, ya sea por restricciones del sistema o por errores en la carga de la imagen.
* **Optimización de Imágenes:** Utiliza imágenes optimizadas para la pantalla de bloqueo en términos de resolución y tamaño para asegurar que se muestren correctamente sin afectar el rendimiento de la aplicación.
* **Compatibilidad:** Ten en cuenta que la personalización de la pantalla de bloqueo puede no estar soportada en todos los dispositivos o versiones de Windows. Siempre verifica la compatibilidad antes de intentar modificarla.

Al seguir estos pasos y consideraciones, podrás implementar una funcionalidad robusta para personalizar la pantalla de bloqueo en tu aplicación UWP, mejorando la experiencia de usuario y proporcionando una interfaz más atractiva y personalizada.
