> 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/acerca-de-about.md).

# Pantalla Acerca de (About)

Como en cualquier aplicación, es interesante presentar al usuario información sobre la misma e incluso hacerle saber cuál es su objetivo y para qué podrá usarla. Como desarrolladores, nos interesa publicitarnos y que la gente nos conozca, que sepan quiénes somos y, por supuesto, que dicha información sea fácilmente compartida.

Es importante también, incluir información relacionada con los autores implicados y los recursos de libre distribución: *Codeplex*, *NuGet*, etc. Su uso implica, en muchos casos incluir sus licencias y hacerlas visibles. En definitiva, se trata del sello de identidad de nuestra aplicación. Ni más ni menos, que nuestro *Copyright*.

Partiremos de la información que ya tenemos en nuestros requisitos, recordando que nuestra pantalla o página de ayuda es la siguiente:

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

**Figura 08.- Pantalla “Acerca de” según la maqueta**

## Creando la página

Como siempre, una vez que tenemos claro el objetivo y disponemos de toda la información, solo tenemos que comenzar diseñar y desarrollar la pantalla.

Recordemos que:

1. En el contenedor de ID *ViewModelLocator* hemos registrado nuestra clase *AboutViewModel* y hemos incluido una propiedad estática *About* para retornar dicho *ViewModel*.
2. La página *AboutPage.xaml*, incluye el enlace a datos a través de su propiedad *DataContext*, para lo que utiliza la clave *Locator*, definida en la clase *App.xaml*.
3. *AboutViewModel* hereda de *ViewModelBase* e implementa la interfaz *IViewModel.*

{% hint style="info" %}
**Nota**: El proceso para la creación de cada nueva página y su respectivo ViewModel será siempre este mismo.
{% endhint %}

Continuaremos añadiendo un control *Pivot*, para representar sus tres secciones o pestañas: General, Licencias y Créditos.

## General

Se trata de la sección principal y estará divida en los siguientes bloques:

* La aplicación. Compuesta por una imagen con el logotipo de la aplicación, el título, la versión y una pequeña descripción.
* El Autor. Contendrá una imagen, el nombre del autor, y las distintas redes sociales y el blog, así como los enlaces para su acceso directo.
* Atribuciones. Son las identificaciones de cada una de las librerías de terceros referenciadas en nuestra solución y con licenciamiento gratuito

En primer lugar, crearemos dos nuevas clases modelo, o entidades, para representar la estructura de información. Las incluiremos en una nueva carpeta *Models*.

```csharp
public class AboutSectionModel
{
    public string Header { get; set; }
    public string ImagePath { get; set; }
    public SectionItem[] Items { get; set; }
}
public class SectionItem
{
    public bool IsLink {get; set;}
    public string Label { get; set; }
    public Uri Link{ get; set; }
    public string Description { get; set; }
}
```

Estas clases van a permitirnos recuperar toda la información desde el *ViewModel* para pasarla a la página XAML a través de una propiedad ***Sections***.

Código C#:

```csharp
public AboutSectionModel[] Sections
{
    get
    {
        var secs = Task.Run(() =>
            FileService.ReadFromApplicationUriAsync<AboutSectionModel[]>(
                "ms-appx:///Assets/AboutSections.json")).Result;
        AboutJsonParse(secs);
        return secs;
    }
}
```

Código XAML:

```xml
<PivotItem x:Uid="AboutPivotItemInfo"
    Header="about">
    <ScrollViewer HorizontalScrollMode="Disabled">
        <ItemsControl ItemsSource="{Binding Sections}"
            ItemTemplate="{ThemeResource AboutSectionsTemplate}">
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Margin"
                        Value="10,12,0,0" />
                    <Setter Property="HorizontalContentAlignment"
                        Value="Stretch" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </ScrollViewer>
</PivotItem>
```

El ViewModel, carga los datos a partir de un fichero de tipo JSON incluido en la carpeta *Assets* como *AboutSections.json*. Utilizando este fichero de texto JSON, podremos cambiarla editando el fichero y sin necesidad de tener que modificar código. Un fragmento de dicho fichero podemos verlo a continuación.

```json
[
{
"Header": "res:AboutApplicationLiteral",
"ImagePath": "/Assets/Logos/MainLogo.png",
"Items": [
{
"Description": "res:AppDisplayName"
},
{
"Description": "res:AppDescription"
},
{
"Description": "pro:AppVersion"
}
]
},
{
"Header": "res:AboutAuthorLiteral",
"ImagePath": "/Assets/Author.jpg",
"Items": [
{
"Description": "res:AboutAuthorName"
},
{
"IsLink": true,
"Label": "Twitter: ",
"Description": "@JuanluElGuerre",
"Link": "https://twitter.com/JuanluElGuerre"
},
...
]
},
. . .
```

Se han indicado prefijos para algunos de sus valores con la intención de disponer únicamente de un fichero JSON para todos los idiomas, y, para la obtención de algunos valores dinámicos.

* res: Identifica a un nombre en los ficheros recursos de idioma.
* pro: Identifica a una propiedad del ViewModel.

En la página, para dar el formato buscado hemos utilizado una plantilla ***AboutSectionsTemplate*** que incluiremos dentro de los recursos de la página (*\<Page.Resources>\<ResourceDictionary>*).

```xml
<DataTemplate x:Key="AboutSectionsTemplate">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Grid.ColumnSpan="2"
            Text="{Binding Header}"
            Style="{StaticResource BaseTextBlockStyle}" />
        <Border Grid.Row="1"
            Margin="0,5,0,0"
            Width="80"
            Height="80"
            IsHitTestVisible="False"
            Visibility="{Binding ImagePath, Mode=OneWay, Converter={StaticResource StringVisibilityConverter}}">
            <Border.Background>
                <SolidColorBrush Color="{ThemeResource SystemAccentColor}" />
            </Border.Background>
            <Image Source="{Binding ImagePath}"
                Stretch="Fill" />
        </Border>
        <ItemsControl Grid.Row="1"
            Grid.Column="1"
            Margin="10,0,0,0"
            ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Label}"
                            Style="{StaticResource CaptionTextBlockStyle}" />
                        <TextBlock Visibility="{Binding IsLink, Converter={StaticResource BoolToVisibilityNegationConverter}}"
                            Style="{StaticResource CaptionTextBlockStyle}"
                            Text="{Binding Description}" />
                        <TextBlock Margin="5,0,0,0"
                            Visibility="{Binding IsLink, Converter={StaticResource BoolToVisibilityConverter}}"
                            Style="{StaticResource CaptionTextBlockStyle}">
                            <Hyperlink NavigateUri="{Binding Link}">
                                <Run Text="{Binding Description}" />
                            </Hyperlink>
                        </TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</DataTemplate>
```

En la plantilla, se muestra u ocultan los elementos del listado, según dispongan o no de un enlace (o *link*), para lo que hemos usado conversores (o ***converters***), que como ya comentamos en el capítulo 4, se encargan de hacer la transformación entre la Vista y el ViewModel durante el enlazado a datos (o *bindings*). Utilizaremos el conversor *BooleanToVisibilityConverter*, ya creado y construiremos uno adicional *BooleanToVisibilityNegationConverter,* para el que, dado un valor booleano *true*, en el *ViewModel* se transforma en un valor *Visibility.Collapse* en la vista.

Para finalizar, registraremos ambos como nuevos recursos, incluyéndolos en el fichero *App.xaml*.

```xml
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:BooleanToVisibilityNegationConverter x:Key="BooleanToVisibilityNegationConverter" />
```

## Licencias

En la sección de licencias, recogeremos todos los textos referentes a las licencias correspondientes a las atribuciones que pueden ser obtenidos a partir de la página web del propio control o DLL. La siguiente figura muestra cómo obtener la licencia MIT para algunas atribuciones.

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

**Figura 09.- Donde obtener los textos de las licencias MVVM Light Toolkit**

Podemos encontrar también los textos de las licencias para los distintos idiomas haciendo una búsqueda rápida en internet, por ejemplo:

* Licencia MIT: <http://es.wikipedia.org/wiki/Licencia_MIT>
* Licencia Publica de Microsoft (Ms-PL): <https://opensource.org/license/ms-pl>

Generaremos un fichero de texto con el contenido de las mismas para cada lenguaje y los incluiremos en la carpeta *Assets*. Nos aseguraremos de establecer el valor “Content” para la propiedad “Build Action”.

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

**Figura 10.- Ficheros de licencias como recursos localizados**

Para la obtención de la información de estos ficheros, incluiremos un atributo de instanciación perezosa, “Lazy\<string>”, de manera que el acceso al fichero se realice justo en el momento de necesitarlo, y no antes, es decir, en el momento en que el usuario se desplace a la sección/pestaña de licencias.

```csharp
private Lazy<string> mitLicense = new Lazy<string>(() =>
    Task.Run(() => FileService.ReadTextFromApplicationUriAsync("ms-appx:///Assets/MIT_LICENSE.TXT")).Result);
…
public string MitLicenseHeader => GetHeader(mitLicense.Value);
public string MitLicense => GetLicense(mitLicense.Value);
…
```

Los métodos *GetHeader* y *GetLicense* obtienen la primera línea del fichero como cabecera o título de la licencia y el texto completo de la misma, respectivamente.

Su representación XAML podemos verla a continuación.

```xml
<PivotItem x:Uid="AboutPivotItemLicense" Header="licenses">
    <ScrollViewer HorizontalScrollMode="Disabled">
        <StackPanel>
            <TextBlock Text="{Binding MitLicenseHeader}"
                Style="{StaticResource BaseTextBlockStyle }" />
            <TextBlock TextWrapping="WrapWholeWords"
                Text="{Binding MitLicense}"
                Style="{StaticResource CaptionTextBlockStyle}" />
            ...
        </StackPanel>
    </ScrollViewer>
</PivotItem>
```

Es recomendable siempre que los objetos que necesitan acceder a distintos recursos: BBDD, ficheros, imágenes y otros que requieran cierto coste en su creación, no se incluyan en el constructor. Puede que se produzca un coste extra al instanciar objetos y nunca se haga uso de tales recursos. En muchos de estos casos, el uso de ***Lazy*** es la solución. Por contra, puede ocurrir que necesitemos todos los datos para hacer cálculos, obtener informes o gráficos, etc., por lo que, podría no ser recomendable.

Para la lectura de los ficheros crearemos un nuevo servicio *FileService*, con los métodos *ReadTextFromApplicationUriAsync* y *ReadFromApplicationUriAsync* que se concentran implementados por el siguiente código a destacar:

```csharp
StorageFile file =
    await StorageFile.GetFileFromApplicationUriAsync(uri);
if (file != null)
{
    return await FileIO.ReadTextAsync(file);
}
```

{% hint style="info" %}
**Nota**: MSDN: “*La utilización de Lazy se recomienda para aplazar la creación de objetos de gran tamaño, de uso intensivo de recursos o para la ejecución de una tarea que consume muchos recursos, particularmente cuando no se puede producir tal creación o ejecución durante el tiempo de vida del programa*.”
{% endhint %}

## Créditos

Participación y agradecimientos de otros autores puntuales, blogueros y otros recursos libres en internet que merecen especial mención. Posiblemente por evitarnos algún que otro dolor de cabeza y porque sin ellos todo sería mucho más complicado.

Incluiremos al igual que para las licencias un fichero para Créditos (CREDITOS.TXT).

## Valoración y Comentarios

La valoración y comentarios (o *rates and feedback*) de nuestra aplicación, podríamos incluirlos como una sección más en nuestro control *Pivot*. No obstante, ya se encarga de esta tarea la Tienda una vez realizada la publicación en la misma.

Una vez descargada e instalada la aplicación, si el usuario quiere valorarla tendrá que acceder nuevamente a la Tienda y buscar la aplicación. Es posible que lleve a cabo esta tarea si pretende valorarla negativamente. Pero, por el contrario, si la aplicación le parece buena o se encuentra satisfecho con ella, probablemente nunca llegue a hacerlo. ¡Básicamente porque no se acuerde! Bastará con que le proporcionemos una opción de menú con acceso directo. Aprenderemos cómo añadir esta opción de menú, en la siguiente sección.

## Opciones de menú

Completada la página, continuaremos con la introducción de las opciones de menú con las acciones: enviar email, compartir información sobre la aplicación, valorar y aportar *feedback*. Añadiremos tres botones a la plantilla *MainCommandBarTemplate*, con tres comandos.

```xml
<DataTemplate x:Key="MainCommandBarTemplate">
    <CommandBar>
        <CommandBar.PrimaryCommands>
            <AppBarButton x:Uid="AboutAppBarButtonMail"
                Icon="Mail"
                Label="mail"
                Command="{Binding AppBarEMailCommand}" />
            <AppBarButton x:Uid="AboutAppBarButtonShare"
                Icon="ReShare"
                Label="share"
                Command="{Binding AppBarShareCommand}" />
            <AppBarButton x:Uid="AboutAppBarButtonFeedback"
                Icon="Comment"
                Label="rating"
                Command="{Binding AppBarFeedbackCommand}" />
        </CommandBar.PrimaryCommands>
    </CommandBar>
</DataTemplate>
```

Los botones se encuentran enlazados con los tres comandos que vemos implementados a continuación, incluidos en la clase *AboutViewModel*:

```csharp
private RelayCommand appBarEmailCommand;
public RelayCommand AppBarEmailCommand
{
    get
    {
        return appBarEmailCommand
            ?? (appBarEmailCommand = new RelayCommand(
                async () =>
        {
            await MailtoService.MailToContactMe();
        }));
    }
}
private RelayCommand appBarShareCommand;
public RelayCommand AppBarShareCommand
{
    get
    {
        return appBarShareCommand
            ?? (appBarShareCommand = new RelayCommand(
                () =>
        {
            SharingService.ShareApp();
        }));
    }
}
private RelayCommand appBarFeedbackCommand;
public RelayCommand AppBarFeedbackCommand
{
    get
    {
        return appBarFeedbackCommand
            ?? (appBarFeedbackCommand = new RelayCommand(
                async () =>
        {
            await FeedbackService.RateAndReviewLaunch();
        }));
    }
}
```

Cada uno de ellos utiliza un **servicio** distinto para acceder a las funcionalidades del dispositivo. En cuanto al nombre, los crearemos con la nomenclatura: **“\<Nombre>Service”**.

### EmailService

El servicio *EmailService*, tal y como su propio nombre indica, es utilizado para el envío de correos.

```csharp
public static async Task MailTo(string emailTo,
    string subject,
    string body = "")
{
    var email = new EmailMessage
    {
        Subject = subject,
        Body = body
    };
    email.To.Add(new EmailRecipient(emailTo));
    await EmailManager.ShowComposeNewEmailAsync(email);
}
public static async Task MailToWithAppTitle(string emailTo,
    string subject,
    string body = "")
{
    await EmailService.MailTo(emailTo,
        $"{LocalizedStrings.AppDisplayName}. {subject}",
        body);
}
public static async Task MailToAuthor(string subject,
    string body = "")
{
    await MailTo(Constants.EMAIL_AUTHOR, subject, body);
}
public static async Task MailToContactMe()
{
    string subject = LocalizedStrings.AboutEmailSubjectSugestions;
    await MailTo(Constants.EMAIL_AUTHOR, subject);
}
```

### SharingService

El servicio Compartir (o *Sharing)* tiene la finalidad de intercambiar información con otras aplicaciones. Se fundamenta en el uso de las declaraciones (contratos y extensiones) que aprendimos en el capítulo 2. Trata de compartir información con otras aplicaciones, por lo que serán éstas, quienes tengan que estar configuradas para concedernos la comunicación.

Por el momento, incluiremos un traspaso de información básico basado en datos de la aplicación incluyendo, como recomendación, el enlace a la Tienda.

```csharp
public static void Share(string title, string text,
    string description = "", string link = "", string imagePath = "")
{
    if (String.IsNullOrWhiteSpace(title))
        throw new ArgumentNullException(nameof(title));
    var dataTransferManager=DataTransferManager.GetForCurrentView();
    dataTransferManager.DataRequested += (sender, e) =>
    {
        DataRequest request = e.Request;
        // Aseguramos que el proceso es asíncrono.
        // Las imágenes pueden demorar el proceso
        DataRequestDeferral deferral = request.GetDeferral();
        request.Data.Properties.Title = title;
        request.Data.Properties.ApplicationName = title;
        request.Data.Properties.Description = description;
        try
        {
            string text = $"{LocalizedStrings.AppDisplayName}. "
                + "{LocalizedStrings.AppDescription}";
            request.Data.SetText(text);
            // El link de la aplicación esta compuesto de la forma:
            // https://www.microsoft.com/store/apps/{appId}
            // El id de aplicación es obtenido al reservar el nombre
            // de aplicación en la tienda.
            request.Data.SetApplicationLink(AppHelper.AppLink);
            request.Data.SetWebLink(AppHelper.AppLink);
        }
        finally
        {
            // El proceso asincrono finaliza
            deferral.Complete();
        }
    };
    DataTransferManager.ShowShareUI();
}
```

Para el intercambio de información utilizamos la clase *DataTransferManager*, perteneciente al espacio de nombres *Windows.ApplicationModel.DataTransfer*. Con el método *ShowShareUI* se inicia la búsqueda de aplicaciones que cumplen el/los contrato/s y se muestra el listado de las mismas. Tras seleccionar una de ellas el evento *DataRequest* completa la transmisión.

### FeedbackService

Para lanzar la valoración de nuestra aplicación desde el menú, utilizaremos la clase *Launcher* a la que pasaremos una URI, que identifica a nuestra aplicación en la Tienda para su revisión.

```csharp
public static async Task RateAndReviewAsync()
{
    if (Microsoft.Toolkit.Uwp.ConnectionHelper.IsInternetAvailable)
    {
        await Launcher.LaunchUriAsync(AppHelper.AppLinkReview);
    }
}
```

Para la obtención de esta URI, así como las utilizadas en el servicio compartir se ha creado una clase de ayuda *AppHelper*.

```csharp
public class AppHelper
{
    private const string appId = Constants.AppId;
    public static Uri AppLink =>
        new Uri($"https://www.microsoft.com/store/apps/{appId}");
    public static Uri AppLinkProtocol =>
        new Uri($"ms-windows-store://pdp/?ProductId={appId}");
    public static Uri AppLinkReview =>
        new Uri($"ms-windows-store://review/?ProductId={appId}");
}
```

Todos los servicios anteriores contienen métodos estáticos, así que por el momento no crearemos ni implementaremos *interfaces*.

## Ejecutando y visualizando en tiempo de diseño

En el capítulo anterior repasamos brevemente los dos escenarios para dotar a la aplicación de información durante la fase de diseño. Basándonos en el segundo de ellos, es decir, en el uso de clases *Mocks,* dotaremos a la clase *AboutViewModelMock*, de información estática, para que podamos visualizar nuestra página mientras la desarrollamos. ¡Seguiremos el principio *What You See Is What You Get* (WYSIWYG), que en español significa: “Lo que ves es lo que obtienes”!

La siguiente figura, muestra la página Acerca de (*About*) en tiempo de diseño desde Visual Studio.

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

**Figura 11.- Página “Acerca de” y pestaña “general” en tiempo de diseño**

Los datos para la vista en tiempo de diseño son obtenidos mediante la implementación de la clase *AboutViewModelMock*, que implementa todas las propiedades públicas definidas en su interfaz *IAboutViewModel*.

```csharp
public AboutSectionModel[] Sections => new AboutSectionModel[]
{
    ...
    new AboutSectionModel()
    {
        ...
    },
    new AboutSectionModel()
    {
        Header = "The author",
        ImagePath = "/Assets/Author.jpg",
        Items = new AboutSectionItem[]
        {
            new AboutSectionItem() {
                Description = "Juan Luis Guerrero Minero" },
            new AboutSectionItem() {
                IsLink =true,
                Label ="Twitter: ",
                Description = "@juanluelguerre",
                Link ="https://twitter.com/JuanluElGuerre"
            },
            new AboutSectionItem() {
                ...
            }
        }
    },
    ...
};
public string AppVersion => "1.0.0.0";
public string MitLicenseHeader => "The MIT License (MIT)";
public string MitLicense =>
    "MIT License sample in design mode ...";
public string ApacheLicense => "Apache-2.0";
public string ApacheLicenseHeader => "Apache-2.0";
public string MPLicenseHeader =>
    "Microsoft Public License (Ms-PL)";
public string MPLicense =>
    "MsPL License sample in design mode ...";
public string CreditsTitle => "Thanks to:";
public string CreditsContent =>
    "JuanluElguerre: https://elguerre.wordpress.com/";
...
```

Para que la clase *Mock*, se ejecute en tiempo de diseño, modificaremos el constructor de la clase *ViewModelLocator* e introduciremos la lógica que determina qué se ejecuta en tiempo de diseño y qué en tiempo de ejecución.

```csharp
static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    current = ServiceLocator.Current;
    if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
    {
        SimpleIoc.Default.Register<IAboutViewModel, AboutViewModelMock>();
    }
    else
    {
        SimpleIoc.Default.Register<IAboutViewModel, AboutViewModel>();
    }
    SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
}
```

El acceso a la página de ayuda, requiere de un botón secundario en el menú *Hamburger,* en la case *Shell.xaml*

```xml
<Controls:HamburgerMenu.SecondaryButtons>
    ...
    <Controls:HamburgerButtonInfo ClearHistory="False"
        PageParameter="0"
        PageType="views:AboutPage">
        <StackPanel Orientation="Horizontal">
            <SymbolIcon Width="48"
                Height="48"
                Symbol="Help" />
            <TextBlock Margin="12,0,0,0"
                VerticalAlignment="Center"
                Text="Help" />
        </StackPanel>
    </Controls:HamburgerButtonInfo>
</Controls:HamburgerMenu.SecondaryButtons>
```

Por último, ejecutamos la aplicación y aseguramos que todo compila y funciona correctamente.

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

**Figura 12.- Ejecución de la página “Acerca de” en un PC**

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

**Figura 13.- Ejecución de la página “Acerca de” en un teléfono**

A continuación, consolidaremos el código en VSTS. La definición de hecho (DOD) está cubierta, así pues, daremos por completado nuestro requisito y estaremos dispuestos para comenzar con el siguiente.
