2010-07-17 2 views
3

J'ai un contrôle utilisateur qui a une grille (celle que vous obtenez automatiquement lorsque vous créez un contrôle utilisateur) et un canevas dans celui-ci.Plusieurs instances de contrôle utilisateur avec un canevas provoquent une exception dans Silverlight

<Grid x:Name="LayoutRoot" Background="White"> 
    <Canvas x:Name="SurfaceCanvas"> 
    </Canvas> 
</Grid> 

Dans le fichier CS, j'ai défini une collection "Items".

public ObservableCollection<TestItem> Items { 
    get { return (ObservableCollection<TestItem>)GetValue(ItemsProperty); } 
    set { SetValue(ItemsProperty, value); } 
} 

public static readonly DependencyProperty ItemsProperty = 
    DependencyProperty.Register("Items", typeof(ObservableCollection<TestItem>), 
    typeof(TestControl), 
    new PropertyMetadata(new ObservableCollection<TestItem>())); 

La déclaration de classe TestItem:

public class TestItem : ContentControl { ... } 

articles sont ajoutés via XAML.

<my:TestControl x:Name="ControlOne" Height="100" Width="100"> 
    <my:TestControl.Items> 
     <my:TestItem x:Name="ItemOne">One</my:TestItem> 
    </my:TestControl.Items> 
</my:TestControl> 

<my:TestControl x:Name="ControlTwo" Height="100" Width="100"> 
    <my:TestControl.Items> 
     <my:TestItem x:Name="ItemTwo">Two</my:TestItem> 
    </my:TestControl.Items> 
</my:TestControl> 

Lorsque des éléments sont ajoutés à la collection, je les ajoute à la zone de dessin.

void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { 
    switch(e.Action) { 
     case NotifyCollectionChangedAction.Add: 
      foreach(Control item in e.NewItems) { 
       SurfaceCanvas.Children.Add(item); 
      } 
     break; 
    } 
} 

Maintenant, le problème.

S'il existe une instance de ce contrôle, tout va bien. Mais quand je définis une seconde instance, je reçois un InvalidOperationException: "L'élément est déjà l'enfant d'un autre élément" sur l'élément ajouté à ControlTwo.

Lorsque je fais un pas à travers et regarder comme éléments sont ajoutés à la toile, ce qui se passe est qu'il crée ItemOne et l'ajoute à ControlOne, puis crée ItemTwo et ajoute à ControlOne avant d'essayer de l'ajouter à ControlTwo. Cela entraîne une exception car vous ne pouvez pas avoir un élément parent avec deux contrôles à la fois. Je suppose que cela a quelque chose à voir avec le fait que le canvas dans chaque instance a le même nom, donc quand il résout "SurfaceCanvas", il récupère deux instances et ajoute à chacune dans l'ordre. Ceci est juste une supposition basée sur l'observation.

Qu'est-ce que je fais mal?

Répondre

3

Ça m'a pris une minute, mais cela est délicat:

Vous avez une valeur par défaut de votre DependencyProperty. La valeur par défaut est créée une seule fois pour votre DependencyProperty, puis affectée aux deux instances de votre TestControl. De cette façon, lorsque vous ajoutez quelque chose à TestControl, vous l'ajoutez au ObservbleCollection commun, qui a maintenant deux délégués d'événement CollectionChanged, chacun ajoutant les nouveaux éléments à leur Canvas respectif. Supprimez la valeur par défaut de ItemsProperty.

Créez un ObservbleCollection dans le constructeur ou l'interpréteur xaml en créera un pour chaque TestControl. Règle: Utilisez une valeur par défaut pour DependencyProperty uniquement avec les types de valeur et non les types de référence.

+0

Merci de poursuivre mes études en une seule étape. – redman

+0

+1, Ceci est un gotcha classique pour tous les DependencyProperties qui ont un type de référence. Cependant, je voudrais un peu assouplir la Règle pour "Utiliser la valeur par défaut seulement pour les types de valeurs __ ou __ pour les références aux immutables". Un bon exemple d'un "immutable" serait un 'String'. – AnthonyWJones

Questions connexes