Enlace de datos con tipos no coincidentes
- 10 minutos
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 de imagen 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 el monto adeudado en una factura y tiene esta propiedad en el objeto de datos.
public decimal BillAmount { get; set; }
El importe adeudado 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. Normalmente, 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, necesita que el objeto de datos le proporcione esa cadena ya formateada como una propiedad diferente, o interceptarla de algún modo y aplicarle usted mismo el formato. 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 que procesa 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:
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 que quiera usar un color en la interfaz de usuario para indicar la seguridad de la contraseña. Hay tres niveles de fuerza: débil, bueno, fuerte. La seguridad se basa en cuántos caracteres tiene la contraseña. Para proporcionar comentarios inmediatos al usuario sobre la fortaleza de su contraseña, quieres que el fondo del control Entry que contiene la contraseña cambie en función de la fortaleza. La imagen siguiente muestra esos tres niveles de fuerza: débil, bueno y fuerte.
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 fortaleza 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
NotSupportedExceptionpara 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);
}
Recordemos que el escenario en el que estamos trabajando consiste en vincular la propiedad Entry.BackgroundColor con la propiedad del objeto de datos PasswordStrength, que es una enumeración Strength.
<Entry BackgroundColor="{Binding PasswordStrength} ... />
La Strength enumeración debe convertirse en un Color, por lo que el convertidor debe evaluar qué valor de Strength se proporciona y devolver un Color diferente. 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
Converttiene 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
valuecontiene el valor entrante. En este ejemplo, es un valor de enumeraciónStrength. - El
targetTypeparámetro se omite, pero puede usar este parámetro para validar que el tipo de propiedad con la que se usa el convertidor es .ColorEsto se omite en este ejemplo por motivos de simplicidad. - Una expresión switch devuelve un color diferente en función del
Strengthvalor.
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 mapea con el 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 {StaticResource} extensión de marcado para hacer referencia a él. El convertidor se asigna a la propiedad de enlace Converter:
<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />