È ansible associare la proprietà Children di Canvas in XAML?

Sono un po ‘sorpreso dal fatto che non sia ansible impostare un binding per Canvas.Children tramite XAML. Ho dovuto ricorrere a un approccio code-behind che assomiglia a questo:

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); } 

Preferirei semplicemente impostare un’associazione in XAML in questo modo:

   

C’è un modo per ottenere questo risultato senza ricorrere a un approccio code-behind? Ho fatto alcuni googling sull’argomento, ma non ho escogitato molto per questo specifico problema.

Non mi piace il mio approccio attuale perché oscura il mio bel Model-View-ViewModel rendendo la View consapevole del suo ViewModel.

Non credo che sia ansible utilizzare il binding con la proprietà Children. In realtà ho provato a farlo oggi e ha commesso un errore su di me come hai fatto tu.

La canvas è un contenitore molto rudimentale. In realtà non è progettato per questo tipo di lavoro. Dovresti esaminare uno dei tanti ItemsControls. È ansible associare la ObservableCollection ViewModel dei modelli di dati alla relativa proprietà ItemsSource e utilizzare DataTemplates per gestire il modo in cui ciascuno degli elementi viene visualizzato nel controllo.

Se non riesci a trovare un ItemsControl che rende i tuoi articoli in modo soddisfacente, potresti dover creare un controllo personalizzato che faccia ciò di cui hai bisogno.

                

Altri hanno dato risposte estensibili su come fare già ciò che realmente si vuole fare. Spiegherò solo perché non è ansible bind direttamente i Children .

Il problema è molto semplice: l’objective di associazione dei dati non può essere una proprietà di sola lettura e Panel.Children è di sola lettura. Non esiste una gestione speciale per le collezioni lì. Al contrario, ItemsControl.ItemsSource è una proprietà di lettura / scrittura, anche se è di tipo di raccolta – un evento raro per una class .NET, ma richiesto in modo da supportare lo scenario di associazione.

ItemsControl è progettato per la creazione di raccolte dinamiche di controlli dell’interfaccia utente da altre raccolte, anche raccolte di dati non dell’interfaccia utente.

È ansible creare un modello di ItemsControl per disegnare su una Canvas . Il modo ideale sarebbe quello di impostare il pannello di supporto su una Canvas e quindi impostare le proprietà Canvas.Left e Canvas.Top sui bambini immediati. Non riuscivo a farlo funzionare perché ItemsControl avvolge i suoi figli con contenitori ed è difficile impostare le proprietà Canvas su questi contenitori.

Invece, uso una Grid come raccoglitore per tutti gli oggetti e li disegno ciascuno sulla propria Canvas . C’è un sovraccarico con questo approccio.

               

Ecco il codice che ho usato per impostare la raccolta di origine:

 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 è una class personalizzata che si comporta proprio come la versione di System . L’ho creato per dimostrare che è ansible utilizzare le proprie classi personalizzate.

Un ultimo dettaglio: è ansible associare la proprietà ItemsSource a qualsiasi raccolta desiderata. Per esempio:

  

Per ulteriori dettagli su ItemsControl e su come funziona, consulta questi documenti: MSDN Library Reference ; Data Templating ; La serie del Dr. WPF su 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); } } } 

E usando: