2009-05-28 8 views
1

Je travaille sur un ListBox qui remplace son ItemsPanelTemplate pour utiliser un Canvas au lieu d'un StackPanel. ListBoxItems avoir un DataTemplate qui utilise un convertisseur pour définir l'apparence et la position de chaque ListBoxItem sur la toile. Lorsque j'ajoute un élément à la collection auquel le ListBox est lié, j'aimerais pouvoir ajouter d'autres UIElement s au canevas. Puis-je accomplir ceci en dehors du convertisseur de ListBoxItem?WPF: ajout de UIElements à un ListBox dont ItemsPanelTemplate est un canevas?

ma section ressources est comme ceci:

<Style TargetType="ListBox"> 
     <Setter Property="ItemsPanel"> 
      <Setter.Value> 
       <ItemsPanelTemplate> 
        <Canvas x:Key="cnvAwesome"> 

        </Canvas> 
       </ItemsPanelTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
    <Style TargetType="ListBoxItem"> 
     <Setter Property="Canvas.Left" Value="{Binding Path=Position.X}" /> 
     <Setter Property="Canvas.Top" Value="{Binding Path=Position.Y}" /> 
    </Style> 

Et ListBox:

<ListBox x:Name="lstAwesome" ItemsSource="{Binding Source={StaticResource someCollection}}"/> 

Et la collection que le ListBox est lié à:

public ObservableCollection<SomeType> someCollection {get; set; } 

Donc, si je avoir une méthode pour ajouter un élément à la collection someCollection que le ListBox lstAwesome est lié à:Donc, est-ce que je peux ajouter des UIElements à un canevas utilisé pour un ItemsPanel par une listbox de databound? Par programme, lorsque j'ajoute des éléments à la collection à laquelle ListBox est lié?

J'apprécierais grandement toute contribution!

Répondre

2

Je pense que vous avez tout en place dans votre question pour ajouter des éléments au Canvas de votre ListBox. Je viens de mettre en place une application de test WPF simple en utilisant le code dans votre question, et cela a juste fonctionné. Chaque fois que j'ai ajouté des éléments au ObservableCollection qui est lié au ItemsSource du ListBox, WPF créerait un ListBoxItem pour cet objet, et basé sur le style du ListBoxItem, l'élément serait dessiné à l'emplacement correct par le Canvas.

N'avez-vous pas réussi à faire fonctionner votre code? Si c'est le cas, quelle erreur avez-vous eu, ou qu'est-ce qui n'a pas fonctionné comme vous l'attendiez?

Édition: Après avoir relu la question, je pense que la confusion est due à une mauvaise compréhension du fonctionnement de la liaison WPF. Lorsque vous liez une collection à ItemsSource de ListBox, tout ce que vous devez faire est d'ajouter des éléments à cette collection (en supposant que la collection implémente INotifyCollectionChanged, ce que fait ObservableCollection). WPF s'abonne aux événements fournis par cette interface et crée/détruit ListBoxItems en fonction des modifications apportées à la liste.

Edit 2: Il serait préférable d'utiliser un pour cette DataTemplate, de sorte que la ligne sera ajoutée automatiquement lorsqu'un élément est ajouté à la collection, je ne sais pas comment vous pourriez obtenir la position de l'élément précédemment ajouté afin que la ligne commence au bon endroit.

Une option qui pourrait fonctionner serait de sous-classer Canvas, et définir cela comme ItemsPanelTemplate pour le ListBox. Je crois qu'il a une fonction que vous pouvez remplacer qui est appelée chaque fois qu'un contrôle y est ajouté. Dans cette fonction, vous pouvez obtenir la position du contrôle précédemment ajouté (qui serait mis en cache dans votre sous-classe Canvas), puis ajouter directement au contrôle de ligne pour les contrôles du Canvas. Cela suppose que la position des contrôles dans le Canvas ne peut pas changer. S'ils le peuvent, vous devrez probablement vous abonner à l'événement PropertyChanged de l'objet et mettre à jour les lignes en conséquence.

Éditer 3: Puisque la liaison ne semble pas une option en raison de vos besoins, la moindre mauvaise option consiste à ajouter les enfants directement. Vous pouvez le faire en effectuant une recherche à travers l'arbre visuel en utilisant VisualTreeHelper.GetChild():

private T FindChild<T>(FrameworkElement parent, string name) 
    where T : FrameworkElement 
{ 
    FrameworkElement curObject = null; 
    T obj = default(T); 
    int count = VisualTreeHelper.GetChildrenCount(parent); 
    for(int i = 0; i < count; i++) 
    { 
     curObject = VisualTreeHelper.GetChild(parent, i) as FrameworkElement; 
     obj = curObject as T; 
     if(null != obj && obj.Name.Equals(name)) 
     { 
      break; 
     } 

     obj = FindChild<T>(curObject, name); 
     if(null != obj) 
     { 
      break; 
     } 
    } 

    return obj; 
} 

Utilisez comme ceci:

Canvas canvas = FindChild<Canvas>(lstAwesome, "cnvAwesome"); 

Ce code recherche récursive à travers l'arbre visuel de parent pour un contrôle du type et prénom. Il serait préférable d'utiliser ici DependencyObject sur FrameworkElement (puisque c'est ce qui est retourné par VisualTreeHelper.GetChild()), cependant Name est seulement défini sur FrameworkElement.

Si vous voulez vous faire plaisir, vous pouvez même en faire une méthode d'extension.

+0

Hey Andy, merci de vérifier cela. Et, oui, les éléments s'affichent correctement pour moi aussi. Ce que je demande, c'est si je peux ajouter des UIElements supplémentaires à la toile, * en plus * des ListItems. Je ne semble pas être en mesure d'obtenir une référence à cnvAwesome (voir xaml) –

+0

Serait-il utile de modifier le ItemsPanelTemplate pour y ajouter les éléments sur le Canvas? Sinon, si vous voulez ajouter un contrôle supplémentaire par ListBoxItem, je vous suggérerais de suivre l'approche d'idursun et de modifier le DataTemplate afin que les contrôles supplémentaires soient automatiquement ajoutés chaque fois que vous ajoutez un élément à la collection. – Andy

+0

Je voudrais suivre l'exemple d'idursun, mais ces UIElements devraient être ajoutés de manière procédurale, lors de l'exécution. Plus précisément, lorsqu'un élément est ajouté à la collection à laquelle ListBox est lié. Je ne peux pas obtenir un handle sur le ItemsPanel que ListBox utilise, et suis incapable d'ajouter UIElements à lui. J'apprécie vraiment l'aide, et s'il vous plaît laissez-moi savoir si je peux rendre mon problème plus clair. –

1

Vous spécifiez la façon dont vos éléments à afficher en définissant un ItemTemplate, comme:

<ListBox.ItemTemplate> 
    <DataTemplate> 
     <Rectangle Width="{Binding Width}" Height="{Binding Height}" /> 
    </DataTemplate> 
</ListBox.ItemTemplate> 

Si vous voulez que votre ItemTemplate être changé en fonction de votre article, vous pouvez alors définir une ItemTemplateSelector et décider qui ItemTemplate à être utilisé par programme.

+0

Je ne pense pas que cette réponse répond à ma question. Si un ListBox utilise un canvas pour son 'ItemsPanel, comment puis-je programmer d'autres UIElements à ce panneau? –

+0

Ensuite, donnez un exemple concret de ce que vous voulez réellement accomplir. – idursun

+0

J'ai modifié mon message original pour plus de clarté. S'il vous plaît laissez-moi savoir si quelque chose n'est pas clair. Merci idursun! –