¿Es posible enlazar una propiedad de Canvas’s Children en XAML?

Estoy un poco sorprendido de que no sea posible configurar un enlace para Canvas.Children a través de XAML. Tuve que recurrir a un enfoque de código subyacente que se ve así:

private void UserControl_Loaded(object sender, RoutedEventArgs e) { DesignerViewModel dvm = this.DataContext as DesignerViewModel; dvm.Document.Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged); foreach (UIElement element in dvm.Document.Items) designerCanvas.Children.Add(element); } private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { ObservableCollection collection = sender as ObservableCollection; foreach (UIElement element in collection) if (!designerCanvas.Children.Contains(element)) designerCanvas.Children.Add(element); List removeList = new List(); foreach (UIElement element in designerCanvas.Children) if (!collection.Contains(element)) removeList.Add(element); foreach (UIElement element in removeList) designerCanvas.Children.Remove(element); } 

Prefiero simplemente configurar un enlace en XAML como este:

   

¿Hay alguna manera de lograr esto sin recurrir a un enfoque de código subyacente? He hecho algunas búsquedas en Google sobre el tema, pero no he encontrado mucho para este problema específico.

No me gusta mi enfoque actual porque arruina mi modelo Model-View-View agradable al hacer que View sea consciente de su ViewModel.

No creo que sea posible usar el enlace con la propiedad Children. De hecho, traté de hacer eso hoy y tuve un error en mí al igual que tú.

The Canvas es un contenedor muy rudimentario. Realmente no está diseñado para este tipo de trabajo. Debería ver uno de los muchos controles de elementos. Puede vincular su ObservableCollection de modelos de datos de ViewModel a su propiedad ItemsSource y usar DataTemplates para controlar cómo se representa cada uno de los elementos en el control.

Si no puede encontrar un ItemsControl que represente sus elementos de manera satisfactoria, es posible que deba crear un control personalizado que haga lo que necesita.

                

Otros han dado respuestas extensibles sobre cómo hacer lo que realmente desea hacer. Solo explicaré por qué no puedes vincular a Children directamente.

El problema es muy simple: el destino de enlace de datos no puede ser una propiedad de solo lectura, y Panel.Children es de solo lectura. No hay un manejo especial para las colecciones allí. Por el contrario, ItemsControl.ItemsSource es una propiedad de lectura / escritura, aunque es de tipo de recostackción, una ocurrencia rara para una clase .NET, pero necesaria para admitir el escenario vinculante.

ItemsControl está diseñado para crear colecciones dinámicas de controles UI de otras colecciones, incluso colecciones de datos que no sean UI.

Puede plantilla un ItemsControl para dibujar en un Canvas . La forma ideal sería configurar el panel de respaldo en un Canvas y luego establecer las propiedades Canvas.Left y Canvas.Top en los elementos Canvas.Left inmediatos. No pude hacer que esto funcione porque ItemsControl envuelve a sus hijos con contenedores y es difícil establecer las propiedades del Canvas en estos contenedores.

En cambio, utilizo una Grid como un recipiente para todos los artículos y los dibujo cada uno en su propio Canvas . Hay algunos gastos generales con este enfoque.

               

Aquí está el código detrás del que usé para configurar la colección fuente:

 List points = new List(); points.Add(new MyPoint(2, 100)); points.Add(new MyPoint(50, 20)); points.Add(new MyPoint(200, 200)); points.Add(new MyPoint(300, 370)); Collection.ItemsSource = points; 

MyPoint es una clase personalizada que se comporta como la versión del System . Lo creé para demostrar que puedes usar tus propias clases personalizadas.

Un último detalle: puede vincular la propiedad ItemsSource a cualquier colección que desee. Por ejemplo:

  

Para obtener más información sobre ItemsControl y cómo funciona, consulte estos documentos: MSDN Library Reference ; Templating de datos ; Serie del Dr. WPF sobre ItemsControl .

 internal static class CanvasAssistant { #region Dependency Properties public static readonly DependencyProperty BoundChildrenProperty = DependencyProperty.RegisterAttached("BoundChildren", typeof (object), typeof (CanvasAssistant), new FrameworkPropertyMetadata(null, onBoundChildrenChanged)); #endregion public static void SetBoundChildren(DependencyObject dependencyObject, string value) { dependencyObject.SetValue(BoundChildrenProperty, value); } private static void onBoundChildrenChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { if (dependencyObject == null) { return; } var canvas = dependencyObject as Canvas; if (canvas == null) return; var objects = (ObservableCollection) e.NewValue; if (objects == null) { canvas.Children.Clear(); return; } //TODO: Create Method for that. objects.CollectionChanged += (sender, args) => { if (args.Action == NotifyCollectionChangedAction.Add) foreach (object item in args.NewItems) { canvas.Children.Add((UIElement) item); } if (args.Action == NotifyCollectionChangedAction.Remove) foreach (object item in args.OldItems) { canvas.Children.Remove((UIElement) item); } }; foreach (UIElement item in objects) { canvas.Children.Add(item); } } } 

Y usando: