Unidades WPF y código subyacente
-
14-11-2019 - |
Pregunta
Recientemente descubrí que WPF admite diferentes unidades de medida en XAML.Además de los DIP predeterminados, también hay soporte para píxeles, pulgadas y centímetros (hasta donde yo sé).Esto permite al diseñador escribir XAML como este:
<Canvas>
<Line X1="0cm" X2="3cm" Y1="1cm" Y2="3cm" Stroke="Black"/>
</Canvas>
Sin embargo, no puede vincular estos valores.Imaginemos que tenemos un ViewModel con la propiedad Dimension que es una Cadena, por ejemplo "7cm".Lo siguiente no funcionará:
<Button Width="{Binding Dimension}">Test</Button>
Se lanza FormatException.De manera similar, al crear un FrameworkElement
en código subyacente, así:
Canvas1.Children.Add(new Button() { Width = "3cm", Content = "Test"});
La compilación falla porque se lanza una excepción en el constructor/dondequiera que intente crear el control.
Me pregunto:
- ¿Es posible utilizar unidades naturales (en mi caso, métricas, centímetros) en el código subyacente?
- ¿Cómo?
- ¿Existe una lista completa de unidades compatibles con WPF/XAML?
EDITAR:
Aquí hay un enlace del comentario a continuación, que resuelve esta pregunta:
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.width.aspx
Solución
Puede hacer esto en el código subyacente aplicando la misma conversión que usa el analizador XAML; no necesita volver a implementar esta lógica si no le importa pasar cadenas:
button.Width = (double)new LengthConverter().ConvertFrom("2cm");
El analizador XAML utiliza Convertidores de tipo para convertir cadenas a los valores necesarios, en el página de documentación del Width
propiedad puedes ver que tiene un TypeConverterAttribute
especificando que un LengthConverter
debería ser usado.Este atributo se puede utilizar para anular localmente cómo se debe manejar una propiedad, pero también se puede aplicar a nivel de clase, por lo que si un Employee
La clase tiene este atributo que especifica que un EmployeeConverter
debe usarse dicho convertidor será el predeterminado para propiedades de tipo Employee
.
Estoy un poco sorprendido de que el enlace no aplique este tipo de convertidor, pero usarlo dentro de un IValueConverter
Sería bastante simple; de hecho, podrías crear un extensión de marcado que construye un IValueConverter
desde un convertidor de tipos para hacerlo bien genérico.
Otros consejos
Para 1) y 2) Debido a que los DIP son independientes del dispositivo, puede calcular cualquier "unidad natural" desde y hacia sin problemas.La excepción son los píxeles (antinaturales :) que dependen del dispositivo.
Crear un Convertidor de valores que convierte valores de cadena en dobles donde se pueden especificar las unidades deseadas en la cadena.Luego puede usar ValueConverter en su Binding y calcular en su ViewModel en "unidades naturales".
Para 3) y la implementación:Busqué rápidamente en Google y no encontré nada, pero tal vez una búsqueda más profunda muestre algo.De lo contrario, crear tu propio convertidor no debería ser muy difícil.Aquí algunas informaciones de conversión:
96DIP == 1inch == 2.54cm;
1cm == 37.8DIP;
1zoll == 1inch == 2.54cm;