que español data wpf xaml data-binding

wpf - español - data binding que es



¿Fuerte vinculación de datos en WPF/Silverlight/XAML? (10)

Una de mis preocupaciones más grandes con respecto a cómo funciona la databinding con XAML es que no hay ninguna opción para escribir con fuerza tus enlaces de datos. En otras palabras, en C #, si desea acceder a una propiedad en un objeto que no existe, no obtendrá ninguna ayuda de Intellisense, y si insiste en ignorar Intellisense, el compilador se quejará de usted y ganará '' Te dejo continuar, y sospecho que mucha gente acordaría que esto es algo muy bueno. Pero en el enlace de datos XAML, estás operando sin una red. Puede vincularse a cualquier cosa , incluso si no existe. De hecho, dada la sintaxis extraña del enlace de datos XAML, y dada mi propia experiencia, es mucho más complicado vincularse a algo que existe que a algo que no existe. Es mucho más probable que mi sintaxis de enlace de datos sea incorrecta que la correcta; y el tiempo comparativo que dedico a la resolución de problemas de las conexiones de datos XAML empequeñece fácilmente el tiempo que paso con cualquier otra porción de la pila de Microsoft (incluida la incómoda y molesta WCF, si puede creerlo). Y la mayor parte de eso (no todo) se debe al hecho de que, sin conexiones de datos fuertemente tipadas, no puedo obtener ayuda de Intellisense o del compilador.

Entonces, lo que quiero saber es por qué la EM al menos no nos da la opción de tener enlaces de datos fuertemente tipados: como en VB6, podríamos hacer cualquier objeto una variante si fuéramos realmente masoquistas, pero la mayoría de los el tiempo tenía sentido usar variables normales, mecanografiadas. ¿Hay alguna razón por la que MS no pueda hacer eso?

Aquí hay un ejemplo de lo que quiero decir. En C #, si la propiedad "UsrID" no existe, recibirá una advertencia de Intellisense y un error del compilador si prueba esto:

string userID = myUser.UsrID;

Sin embargo, en XAML, puedes hacer todo lo que quieras:

<TextBlock Text="{Binding UsrID}" />

Y ni Intellisense, el compilador ni (más asombrosamente) la aplicación en tiempo de ejecución le darán ninguna pista de que ha hecho algo mal. Ahora bien, este es un ejemplo simplista, pero cualquier aplicación del mundo real que trate con gráficos de objetos complejos e interfaces de usuario complejas tendrá muchos escenarios equivalentes que no son simples en absoluto ni fáciles de solucionar. E incluso después de haberlo hecho funcionar correctamente la primera vez, usted es SOL si refactoriza su código y cambia sus nombres de propiedad C #. Todo se compilará y se ejecutará sin ningún error, pero nada funcionará, dejándote cazar y picotear a través de toda la aplicación, tratando de descubrir qué está roto.

Una posible sugerencia (fuera de mi cabeza, y que no he pensado) podría ser algo como esto:

Para cualquier parte del árbol lógico, puede especificar en XAML el DataType del objeto que espera, así:

<Grid x:Name="personGrid" BindingDataType="{x:Type collections:ObservableCollection x:TypeArgument={data:Person}}">

Esto quizás genere una propiedad de TybleDataContext <Person> TypedDataContext fuertemente tipada en el archivo .g.cs. Entonces en tu código:

// This would work personGrid.TypedDataContext = new ObservableCollection<Person>(); // This would trigger a design-time and compile-time error personGrid.TypedDataContext = new ObservableCollection<Order>();

Y si luego accediste a ese TypedDataContext a través de un control en la grilla, sabría a qué tipo de objeto intentabas acceder.

<!-- It knows that individual items resolve to a data:Person --> <ListBox ItemsSource="{TypedBinding}"> <ListBox.ItemTemplate> <DataTemplate> <!--This would work --> <TextBlock Text="{TypedBinding Path=Address.City}" /> <!-- This would trigger a design-time warning and compile-time error, since it has the path wrong --> <TextBlock Text="{TypedBinding Path=Person.Address.City} /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

He hecho una publicación de blog here que explica más acerca de mis frustraciones con el enlace de datos WPF / XAML, y lo que creo que sería un enfoque significativamente mejor. ¿Hay alguna razón por la cual esto no podría funcionar? ¿Alguien sabe si MS está planeando solucionar este problema (en la línea de mi propuesta, o con suerte, una mejor)?


¡Esta es mi mayor queja con XAML! No tener el compilador aplicando enlaces de datos válidos es un GRAN problema. Realmente no me importa intellisense, pero sí me importa la falta de apoyo de refactorización.

Cambiar los nombres o tipos de propiedad es peligroso en una aplicación WPF: el uso del soporte de refactorización incorporado no actualizará los enlaces de datos en XAML. Hacer una búsqueda y reemplazar el nombre es peligroso, ya que puede cambiar el código que no pretendía cambiar. Revisar una lista de resultados de búsqueda es una tarea dolorosa y lenta.

MVC ha tenido vistas fuertemente tipadas durante un tiempo: el proyecto contrib de MVC las proporcionó para MVC1 y MVC2 las proporciona de forma nativa. XAML debe admitir esto en el futuro, especialmente si se usa en proyectos "ágiles" en los que el diseño de una aplicación evoluciona con el tiempo. No he visto .NET 4.0 / VS2010, ¡pero espero que la experiencia sea mucho mejor de lo que es!



Además, si tiene su ventana de salida abierta cuando está depurando su proyecto, VS le informará de cualquier error de enlace de datos, es decir, que la propiedad a la que está sujeto un control no existe.


Chicos, lo que Ken y Grant intentan decir es ...

¿Qué tal un XAMl donde puedo hacer p.UserId)> donde P es el DataContext de tipo Cliente


De acuerdo, no pude resistir, aquí está el enfoque de Propiedad adjunta:

Aquí está el XAML:

<phone:PhoneApplicationPage.Resources> <!-- let''s pretend this is your data source --> <CollectionViewSource x:Key="MyViewSource" Source="{Binding}" converters:RestrictType.Property="Source" converters:RestrictType.Type="System.Int16" /> </phone:PhoneApplicationPage.Resources>

Aquí está el código de propiedad:

public class RestrictType { // type public static String GetType(DependencyObject obj) { return (String)obj.GetValue(TypeProperty); } public static void SetType(DependencyObject obj, String value) { obj.SetValue(TypeProperty, value); Watch(obj); } public static readonly DependencyProperty TypeProperty = DependencyProperty.RegisterAttached("Type", typeof(String), typeof(RestrictType), null); // property public static String GetProperty(DependencyObject obj) { return (String)obj.GetValue(PropertyProperty); } public static void SetProperty(DependencyObject obj, String value) { obj.SetValue(PropertyProperty, value); Watch(obj); } public static readonly DependencyProperty PropertyProperty = DependencyProperty.RegisterAttached("Property", typeof(String), typeof(RestrictType), null); private static bool m_Watching = false; private static void Watch(DependencyObject element) { // element must be a FrameworkElement if (element == null) System.Diagnostics.Debugger.Break(); // let''s not start watching until each is set var _PropName = GetProperty(element); var _PropTypeName = GetType(element); if (_PropName == null || _PropTypeName == null) return; // we will not be setting this up twice if (m_Watching) return; m_Watching = true; // listen with a dp so it is a weak reference var _Binding = new Binding(_PropName) { Source = element }; var _Prop = System.Windows.DependencyProperty.RegisterAttached( "ListenToProp" + _PropName, typeof(object), element.GetType(), new PropertyMetadata((s, e) => { Test(s); })); BindingOperations.SetBinding(element, _Prop, _Binding); // run now in case it is already set Test(element); } // test property value type static void Test(object sender) { // ensure element type (again) var _Element = sender as DependencyObject; if (_Element == null) System.Diagnostics.Debugger.Break(); // the type must be provided var _TypeName = GetType(_Element); if (_TypeName == null) System.Diagnostics.Debugger.Break(); // convert type string to type Type _Type = null; try { _Type = Type.GetType(_TypeName); if (_Type == null) System.Diagnostics.Debugger.Break(); } catch { System.Diagnostics.Debugger.Break(); } // the property name must be provided var _PropName = GetProperty(_Element); if (string.IsNullOrWhiteSpace(_PropName)) System.Diagnostics.Debugger.Break(); // the element must have the specified property var _PropInfo = _Element.GetType().GetProperty(_PropName); if (_PropInfo == null) System.Diagnostics.Debugger.Break(); // the property''s value''s Type must match var _PropValue = _PropInfo.GetValue(_Element, null); if (_PropValue != null) if (_PropValue.GetType() != _Type) System.Diagnostics.Debugger.Break(); } }

¡La mejor de las suertes! Simplemente pasándolo bien.


Habrá soporte de IntelliSense para el enlace de datos en Visual Studio 2010. Parece que su queja se reduce a esto, ya que el enlace de datos está fuertemente tipado. Simplemente no averigua, hasta el momento de la ejecución, si el enlace tuvo éxito o no, y en la mayoría de los casos falla silenciosamente en lugar de hacerlo con una ruidosa excepción. Cuando falla un enlace, WPF volca el texto explicativo a través de los rastreos de depuración, que se pueden ver en la ventana de resultados de Visual Studio.

Además de la falta de soporte de IntelliSense y algunos problemas de sintaxis extraños, el enlace de datos está bastante bien hecho (al menos en mi opinión). Para más ayuda con la depuración de enlaces de datos, vería el precioso artículo de Bea here .


Ken, C # se beneficiaría de un elemento sintáctico conciso para hacer referencia a una clase PropertyInfo. Las estructuras PropertyInfo son objetos estáticos definidos en tiempo de compilación y, como tales, proporcionan una clave única para cada propiedad de un objeto. Las propiedades podrían validarse en el momento de la compilación.

El único problema con esto es la rareza de tratar una instancia de un objeto como un tipo de datos dado que la tipificación fuerte se aplica a los tipos, no a los valores de un tipo. Tradicionalmente, los compiladores no imponen valores de datos, y en su lugar confían en el código de tiempo de ejecución para verificar sus datos. En la mayoría de los casos, ni siquiera es posible validar los datos en tiempo de compilación, pero la reflexión es uno de esos casos extremos en los que, al menos, es posible.

Alternativamente, el compilador podría crear un nuevo tipo de datos para cada propiedad. Me imagino que se crearían muchos tipos, pero eso permitiría la compilación en tiempo de enlace de propiedades.

Una forma de pensarlo es que el CLR introdujo un nivel de reflexión que era otro nivel de magnitud en comparación con los sistemas que lo precedieron. Ahora se está utilizando para hacer cosas bastante impresionantes, como el enlace de datos. Pero su implementación todavía se encuentra en un nivel de metadatos, un tipo de informe del compilador para cada tipo de datos que se genera. Supuse que una forma de crecer C # sería promover los metadatos para compilar la verificación del tiempo.

Me parece que alguien podría desarrollar una herramienta de compilación que agregue esa validación del nivel de reflexión. El nuevo intellisense es así. Sería complicado descubrir genéricamente los parámetros de cadena que están destinados a ser comparados con PropertyInfos, pero no es imposible. Se podría definir un nuevo tipo de datos como "PropertyString" que identifique claramente los parámetros que se compararán con PropertyInfos en el futuro.

De todos modos, siento tu dolor. He perseguido muchas referencias de nombres de propiedades mal escritas. Honestamente, hay muchas irritaciones en WPF relacionadas con la reflexión. Una herramienta útil sería un verificador de cumplimiento de WPF que se asegure de que todos sus constructores de control estáticos estén en su lugar, sus atributos definidos correctamente, los enlaces sean precisos, claves correctas, etc. Hay una larga lista de validaciones que podrían realizarse.

Si todavía estuviera trabajando para Microsoft, probablemente intentaría hacerlo.


Parece que lo que está solicitando no requerirá ningún cambio en el marco, y posiblemente ya esté solucionado en VS 2010 (no lo tengo instalado).

El diseñador de XAML necesita IntelliSense para los enlaces dentro de un DataTemplate cuando se especifica un DataType, que ya es posible así:

<DataTemplate DataType="{x:Type data:Person}"> ... </DataTemplate>

Estoy de acuerdo en que tener IntelliSense en este caso sería un cambio útil, pero sus otras sugerencias parecen perder el punto de poder cambiar DataContexts en tiempo de ejecución y usar DataTemplates para diferentes tipos para que se conviertan en únicos.


Siento que xaml es algo así como old-days html. No me puedo imaginar que después de más de 10 años, estoy haciendo programación de esa manera: escribiendo etiquetas de apertura y cierre manualmente porque no puedo tener una GUI madurada para definir estilos, enlaces y plantillas. Apoyo firmemente su punto de vista, Ken. Me siento muy raro por qué tantas personas están apoyando MVVM sin una sola queja sobre el dolor en la depuración xaml. El enlace de comandos y el enlace de datos son conceptos muy buenos y he estado diseñando mis aplicaciones de winform de esa manera. Sin embargo, la solución de enlace xaml junto con algún otro problema xaml (o la falta de una característica sofisticada) es realmente una gran falla en VS. Hizo que el desarrollo y la depuración fueran muy difíciles e hizo que el código fuera muy ilegible.


¡Esta es realmente una solución para lo que estás buscando!

Pero no es una solución de marco nativa incorporada. Sí, creo que eso es lo que realmente queremos todos aquí. Quizás lo consigamos más tarde.

Mientras tanto, si estás decidido a restringir el tipo, ¡esto resuelve eso!

Usando este convertidor:

public class RequireTypeConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) return value; // user needs to pass a valid type if (parameter == null) System.Diagnostics.Debugger.Break(); // parameter must parse to some type Type _Type = null; try { var _TypeName = parameter.ToString(); if (string.IsNullOrWhiteSpace(_TypeName)) System.Diagnostics.Debugger.Break(); _Type = Type.GetType(_TypeName); if (_Type == null) System.Diagnostics.Debugger.Break(); } catch { System.Diagnostics.Debugger.Break(); } // value needs to be specified type if (value.GetType() != _Type) System.Diagnostics.Debugger.Break(); // don''t mess with it, just send it back return value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

Luego, restringe tu DataType de esta manera:

<phone:PhoneApplicationPage.Resources> <!-- let''s pretend this is your data source --> <CollectionViewSource x:Key="MyViewSource" Source="{Binding}"/> <!-- validate data type - start --> <converters:RequireTypeConverter x:Key="MyConverter" /> <TextBlock x:Key="DataTypeTestTextBlock" DataContext="{Binding Path=., Source={StaticResource MyViewSource}, Converter={StaticResource MyConverter}, ConverterParameter=System.Int16}" /> <!-- validate data type - end --> </phone:PhoneApplicationPage.Resources>

¿Ve cómo estoy requiriendo que CollectionViewSource tenga System.Int16? Por supuesto, ni siquiera puede establecer el Origen de un CVS en un Entero, por lo que siempre fallará. Pero eso prueba el punto con seguridad. Es una lástima que Silverlight no admita {x: Type} o podría haber hecho algo como ConverterParameter = {x: Type sys: Int16} que hubiera sido agradable.

Además, ese XAML no debe ser intrusivo, por lo que debería poder implementarlo sin ningún riesgo. Si el tipo de datos es siempre lo que no desea que sea, entonces el depurador se romperá y usted puede patearse por romper su propia regla. :)

De nuevo, sé que esto es un poco raro, pero hace lo que quieres; de hecho, he estado jugando con él mientras lo codificaba y tal vez incluso tengo un uso para él. Me gusta que sea solo tiempo de diseño / depuración. Pero, escucha, no estoy tratando de vendértelo.

Solo me estoy divirtiendo, si esto es demasiada sintaxis, solo disfruta de mi esfuerzo;)

PD: también puede crear una propiedad adjunta de Tipo tipo que pueda usar de la misma manera. Podría adjuntarlo a su CVS o cualquier otra cosa, como x: RequiredType = "System.Int16" y en el comportamiento de la propiedad podría simplemente repetir la lógica del convertidor. Mismo efecto, probablemente la misma cantidad de código, pero otra opción viable si habla en serio.