É possível vincular uma propriedade de Canvas’s Children em XAML?

Estou um pouco surpreso que não é possível configurar uma binding para Canvas.Children através de XAML. Eu tive que recorrer a uma abordagem code-behind que se parece com algo assim:

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

Eu prefiro apenas configurar uma binding em XAML assim:

   

Existe uma maneira de conseguir isso sem recorrer a uma abordagem por código? Eu fiz algumas pesquisas sobre o assunto, mas não encontrei muito para este problema específico.

Eu não gosto da minha abordagem atual, porque isso faz com que o meu Model-View-ViewModel seja agradável, tornando o View ciente do ViewModel.

Não acredito que seja possível usar a binding com a propriedade Children. Eu realmente tentei fazer isso hoje e isso me enganou como aconteceu com você.

O Canvas é um contêiner muito rudimentar. Realmente não é projetado para esse tipo de trabalho. Você deve procurar em um dos muitos ItemsControls. Você pode vincular o ObservableCollection de modelos de dados do ViewModel à sua propriedade ItemsSource e usar DataTemplates para manipular como cada um dos itens é processado no controle.

Se você não conseguir encontrar um ItemsControl que renderize seus itens de maneira satisfatória, talvez seja necessário criar um controle personalizado que faça o que você precisa.

                

Outros deram respostas extensíveis sobre como fazer o que você realmente quer fazer. Eu só vou explicar porque você não pode amarrar as Children diretamente.

O problema é muito simples – destino de binding de dados não pode ser uma propriedade somente leitura e Panel.Children é somente leitura. Não há tratamento especial para collections lá. Por outro lado, ItemsControl.ItemsSource é uma propriedade de leitura / gravação, mesmo sendo do tipo coleção – uma ocorrência rara para uma class .NET, mas necessária para suportar o cenário de binding.

ItemsControl foi projetado para criar collections dinâmicas de controles da interface do usuário de outras collections, mesmo de collections de dados não-da interface do usuário.

Você pode criar um modelo de ItemsControl para desenhar em uma Canvas . A maneira ideal envolveria configurar o painel de apoio para um Canvas e, em seguida, definir as propriedades Canvas.Top e Canvas.Top nos filhos imediatos. Não consegui fazer isso funcionar porque o ItemsControl envolve seus filhos com contêineres e é difícil definir as propriedades do Canvas nesses contêineres.

Em vez disso, eu uso uma Grid como uma lixeira para todos os itens e desenhe cada um em sua própria Canvas . Existe alguma sobrecarga com essa abordagem.

               

Aqui está o código por trás que eu usei para configurar a coleção de fonts:

 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 é uma class personalizada que se comporta exatamente como a versão do System . Eu criei para demonstrar que você pode usar suas próprias classs personalizadas.

Um detalhe final: você pode vincular a propriedade ItemsSource a qualquer coleção desejada. Por exemplo:

  

Para obter mais detalhes sobre o ItemsControl e como ele funciona, confira estes documentos: MSDN Library Reference ; Modelagem de dados ; Série do Dr. WPF em 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: