Enlace de datos con tipos no coincidentes

Completado

A veces, los datos que usa no coinciden con el tipo de datos de la propiedad de control que muestra los datos. Por ejemplo, puede tener un valor monetario almacenado en un tipo decimal que desee mostrar en un control Label, con formato de moneda. Un ejemplo más complicado sería con la aplicación meteorológica presentada en el módulo. Se supone que se debe mostrar una imagen en función del valor de enumeración Sunny o Cloudy del pronóstico meteorológico, pero no se puede enlazar el valor de enumeración del origen a la propiedad image de un destino. En esta unidad, se examinan las formas en que puede convertir datos.

Formato de cadena

Un error de coincidencia de tipos común es un tipo intrínseco que se quiere mostrar como una cadena con formato. Al igual que cuando desea mostrar partes de un valor DateTime o desea dar formato a un tipo decimal como moneda.

Supongamos que desea mostrar la cantidad vencida en una factura y tiene esta propiedad en el objeto de datos:

public decimal BillAmount { get; set; }

El importe permitido termina siendo 22.0304. Puede usar dos controles de etiqueta para mostrar texto y la cantidad de dólar, como se muestra en el siguiente fragmento de código:

<HorizontalStackLayout>
    <Label Text="You owe" Margin="0,0,5,0" />
    <Label Text="{Binding BillAmount}" />
    <Label Text="to the bank" Margin="5,0,0,0" />
</HorizontalStackLayout>

Esto genera una cadena en la interfaz de usuario que tiene un aspecto similar a You owe 22.0304 to the bank, pero falta el símbolo de moneda y puede haber demasiados o muy pocos decimales en función de la moneda local. Por lo general, procesaría el valor como una cadena con el especificador de formato "C" (o moneda) en el código, de la siguiente manera:

string formattedBillAmount = string.Format("{0:C}", BillAmount);

Sin embargo, para usar el formato en el enlace de datos, debe tener el objeto de datos que le proporcione esa cadena con formato como una propiedad diferente, o interceptarlo de alguna manera y formatearlo usted mismo. Afortunadamente, los enlaces .NET MAUI proporcionan una manera de dar formato a las cadenas con la propiedad de enlace StringFormat. La cadena de formato sigue las mismas reglas que el método String.Format. Incluya la cadena de formato entre comillas simples para que el analizador de XAML no se confunda con las llaves. El parámetro de formato de cadena 0 es el valor de propiedad procesado por el enlace.

<Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />

Tenga en cuenta el código XAML siguiente, que demuestra la visualización de BillAmount utilizando ambas formas:

<VerticalStackLayout Padding="10">
    <HorizontalStackLayout>
        <Label Text="You owe" Margin="0,0,5,0" />
        <Label Text="{Binding BillAmount}" />
        <Label Text="to the bank" Margin="5,0,0,0" />
    </HorizontalStackLayout>
    <HorizontalStackLayout>
        <Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />
    </HorizontalStackLayout>
</VerticalStackLayout>

En la imagen siguiente, se muestra lo que genera la salida XAML en la pantalla:

Captura de pantalla de una aplicación .NET MAUI Android que muestra dos controles de etiqueta diferentes con texto relacionado con debido a una factura. El texto de una etiqueta tiene el formato de moneda USD.

El XAML que usa la propiedad de enlace StringFormat es más sencillo que el otro XAML y tiene acceso al sistema eficaz de formato de cadenas de .NET.

Conversión de tipos personalizados

La propiedad de enlace StringFormat es conveniente al mostrar un valor como una cadena, pero no cuando se quiere convertir algo como un Color o Image de otro tipo. En estos casos, debe escribir código de conversión personalizado.

Supongamos que pide al usuario que elija una contraseña y quiere usar un color en la interfaz de usuario para indicar la seguridad de la contraseña. Hay tres niveles de seguridad: débil, bueno, fuerte. La seguridad se basa en cuántos caracteres tiene la contraseña. Para proporcionar comentarios inmediatos al usuario sobre la seguridad de su contraseña, debe hacer que el fondo del control Entry que contiene la contraseña cambie en función de la seguridad. En la imagen siguiente, se muestran estos tres niveles de seguridad: débil, bueno y fuerte.

Captura de pantalla de una aplicación .NET MAUI Android con tres controles de entrada, cada uno con un fondo de color diferente.

De los tres controles de entrada de la captura de pantalla, el primero tiene cuatro caracteres especificados y presenta un fondo rojo. El segundo tiene nueve caracteres introducidos y cuenta con un fondo amarillo. El último control de entrada tiene 15 caracteres y cuenta con un fondo azul.

Estos niveles se asignan a la enumeración Strength:

private enum Strength
{
    Weak,
    Good,
    Strong
}

Se asigna un objeto de datos como BindingContext de la página, que contiene la seguridad de la contraseña con la propiedad PasswordStrength. A medida que el usuario escribe una contraseña, la propiedad PasswordStrength se cambia según la longitud de la contraseña. Dado que el objeto de datos contiene la propiedad PasswordStrength, enlace esa propiedad al BackgroundColor del control Entry:

<Entry BackgroundColor="{Binding PasswordStrength} ... />

Sin embargo, hay un problema aquí. PasswordStrength es de tipo Strength mientras que BackgroundColor es Color. Estos tipos no son compatibles entre sí. .NET MAUI proporciona una manera de corregir estos problemas de coincidencia de tipos, la propiedad del enlace Converter.

Un convertidor de enlaces hace exactamente lo que dice su nombre, convierte entre el origen de enlace y el destino. Los convertidores se implementan a través de la interfaz IValueConverter.

Implementación de IValueConverter

La lógica de conversión se crea en una clase que implementa la interfaz IValueConverter. Por lo general, los nombres de estas clases terminan con el nombre Converter para que su propósito sea claro.

La interfaz IValueConverter define dos métodos:

  • Convert: convierte de la propiedad del origen de enlace a la propiedad de interfaz de usuario del destino de enlace.

  • ConvertBack: convierte de nuevo la propiedad de la interfaz de usuario del destino de enlace a la propiedad del origen de enlace.

    Este método no se suele usar y no se usa en este escenario. La mayoría de los convertidores inician un NotSupportedException para indicar que no se admite esta conversión.

Este es el contrato de la interfaz:

public interface IValueConverter
{
    object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
    object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture);
}

Recuerde que el escenario con el que estamos trabajando es enlazar la propiedad Entry.BackgroundColor a la propiedad del objeto de datos PasswordStrength, que es una enumeración Strength.

<Entry BackgroundColor="{Binding PasswordStrength} ... />

La enumeración Strength debe convertirse en un Color. Por tanto, el convertidor debe evaluar qué valor Strength se proporciona y devolver otro Color. El código siguiente muestra esta conversión:

namespace MyProject.Converters;

class StrengthToColorConverter : IValueConverter
{
    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        return (Strength)value! switch
        {
            Strength.Weak => Colors.OrangeRed,
            Strength.Good => Colors.Yellow,
            Strength.Strong => Colors.LightBlue,
            _ => Colors.LightBlue
        };
    }

    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
        throw new NotImplementedException();
}

Vamos a desglosar este código:

  • El método Convert tiene cuatro parámetros. Por lo general, puede descartar los dos últimos parámetros a menos que tenga una razón específica para usarlos.
  • El parámetro value contiene el valor entrante. En este ejemplo, es un valor de enumeración Strength.
  • Se omite el parámetro targetType. Sin embargo, podría usar este parámetro para validar que el tipo de propiedad con la que se usa el convertidor es Color. Esto se omite en este ejemplo por motivos de simplicidad.
  • Se usa una expresión switch para devolver un color diferente en función del valor Strength.

Uso del convertidor en XAML

Con la clase de convertidor creada, debe crear una instancia de ella y hacer referencia a ella en el enlace. La forma estándar de crear una instancia del convertidor está en el diccionario de recursos del elemento raíz.

En primer lugar, asigne un espacio de nombres XML al espacio de nombres de .NET que contiene el convertidor. En el ejemplo de código siguiente, el espacio de nombres XML cvt se asigna al espacio de nombres .NET MyProject.Converters:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             ...

A continuación, cree una instancia en ContentPage.Resources:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             x:Class="MyProject.PasswordExample">
    <ContentPage.Resources>
        <cvt:StrengthToColorConverter x:Key="StrengthToColorConverter" />
    </ContentPage.Resources>

Ahora una instancia del convertidor está en el diccionario de recursos con la clave StrengthToColorConverter, que tiene el mismo nombre que el tipo. Esta es una forma típica de asignar nombres a los convertidores, ya que, por lo general, solo tiene un único convertidor que reutiliza en todo el XAML. Si, por algún motivo, requería varias instancias del convertidor, las claves tendrían que ser diferentes entre ellas.

Por último, haga referencia al convertidor en el enlace. Dado que el convertidor está en un diccionario de recursos, se usa la extensión de marcado {StaticResource} para hacer referencia a él. El convertidor se asigna a la propiedad de enlace Converter:

<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />