2012-03-19 5 views
1

classe simpleComment forcer Unicité Dans un ObservableCollection

public class Group 
{ 
    public Int16 ID { get; private set; } 
    public string Name { get; set; } 
    public Group (Int16 id, string name) 
    { ID = id; Name = name; } 
} 

Ce que je voudrais est un ObservableCollection où les forces de l'unicité collection ID unique et CASEINSENSITIVE sur Nom.

Ce que j'ai essayé est:

public class Group 
{ 
    public Int16 ID { get; private set; } 
    public string Name { get; set; } 

    public override bool Equals(System.Object obj) 
    { 
     // If parameter is null return false. 
     if (obj == null) 
     { 
      return false; 
     } 

     // If parameter cannot be cast to Point return false. 
     Group g = obj as Group; 
     if ((System.Object)g == null) 
     { 
      return false; 
     } 

     // Return true if either fields match: 
     return (ID == g.ID || string.Compare(Name, g.Name, true) == 0) ; 
    } 

    public bool Equals(Group g) 
    { 
     // If parameter is null return false: 
     if ((object)g == null) 
     { 
      return false; 
     } 

     // Return true if either fields match: 
     return (ID == g.ID || string.Compare(Name, g.Name, true) == 0) ; 
    } 

    public override int GetHashCode() 
    { 
     return ID; //^(Int32)Name.ToLower(); 
    } 

    public Group (Int16 id, string name) 
    { ID = id; Name = name; } 
} 

HashSet &lt Group &gt 

qui empêche l'ajout d'un groupe avec le même ID ne doe empêche pas d'ajouter un groupe avec le même nom. Et il n'arrête pas de renommer un nom en un nom existant.

+0

J'ai un peu de mal à voir ce que vous voulez faire avec le comportement de collision de la classe Group (l'édition du sous-groupe provoquera-t-elle une collision?). Pouvez-vous poster un exemple qui utilise Group dans le comportement que vous voulez et montrer ce qui le ferait signaler qu'il y avait une collision? @message moi quand vous faites, si vous faites ce que je pense que vous faites, je peux avoir du code qui fait exactement ce que vous voulez. –

+0

@ScottChamberlain Je ne m'inquiète pas vraiment de la façon dont elle impose l'unicité. Il peut lancer une erreur ou simplement ne pas effectuer l'action. S'il n'effectue pas l'action, il doit prendre en charge .contains pour pouvoir vérifier. Avec GetHashCode d'ID et HashSet, il fournit unicité sur l'ID et .add ne fait simplement rien sur un ID répété mais .Contains donne la bonne réponse (pour ID seul). Mais cela ne fait rien pour le nom qui est plus complexe car il a un ensemble public. – Paparazzi

Répondre

2

Il faudra un peu de peaufinage de votre classe, mais voici comment le faire. Vous devez d'abord indiquer à la collection que vous êtes sur le point de modifier le nom, en ajoutant un événement et en modifiant le paramètre Nom.

Première Ajouter cette interface et gestionnaire d'événements.

public delegate void TestForColisions(object sender, TestForColisionsArgs e); 

public class TestForColisionsArgs : CancelEventArgs 
{ 
    public TestForColisionsArgs(object newValue) 
    { 
     NewValue = newValue; 
    } 

    public object NewValue { get; private set; } 
} 


public interface ITestForColisions 
{ 
    /// <summary> 
    /// Set the event to Canceled if there will be a collision. 
    /// </summary> 
    event TestForColisions TestForCollision; 
} 

Testez ensuite votre classe implémente l'interface

public class Group : ITestForColisions, IEquateable<Group> 
{ 
    public Int16 ID { get; private set; } 

    private string _Name; 
    public string Name 
    { 
     get { return _Name; } 
     set 
     { 
      //If RaiseNameChanging returns true there was a collision. 
      if (RaiseNameChanging(value)) 
      { 
       throw new ArgumentException(String.Format("The name {0} is in use in the collection", value)); 
      } 
      else 
      { 
       _Name = value; 
      } 
     } 
    } 

    protected virtual bool RaiseNameChanging(string name) 
    { 
     //Make a copy with the new name. 
     var newGroup = (Group)this.MemberwiseClone(); 
     newGroup.Name = name; 

     var cancelEventArgs = new TestForColisionsArgs(newGroup); 
     if (TestForCollision != null) 
     { 
      TestForCollision(this, cancelEventArgs); 
     } 
     return cancelEventArgs.Cancel; 
    } 

    //(...) 
} 

Ensuite, vous aurez besoin d'une collection personnalisée qui écoute les événements TestForCollision et gère en conséquence. Pour la plupart des méthodes, vous pouvez simplement appeler la version _BaseSet parent, mais pour l'une des méthodes qui modifient l'ensemble, vous devrez vous abonner ou vous désabonner à l'événement. J'ai fait Effacer, ajouter et supprimer pour vous.

public class ColisionTestedCollection<T> : ISet<T> 
    where T : ITestForColisions 
{ 
    public ColisionTestedCollection(ISet<T> baseSet) 
    { 
     _BaseSet = baseSet; 
     _EqualityComparer = EqualityComparer<T>.Default; 
    } 

    public ColisionTestedCollection(ISet<T> baseSet, IEqualityComparer<T> equalityComparer) 
    { 
     _BaseSet = baseSet; 
     _EqualityComparer = equalityComparer; 
    } 

    private ISet<T> _BaseSet; 
    private IEqualityComparer<T> _EqualityComparer; 


    void TestItemsForCollision(object sender, TestForColisionsArgs e) 
    { 
     if (_BaseSet.Contains((T)e.NewValue, _EqualityComparer)) 
     { 
      e.Cancel = true; 
     } 
    } 

    public bool Add(T item) 
    { 
     bool added = _BaseSet.Add(item); 
     if(added) 
      item.TestForCollision += TestItemsForCollision; 
     return added; 
    } 

    void ICollection<T>.Add(T item) 
    { 
     ((ICollection<T>)_BaseSet).Add(item); 
     item.TestForCollision += TestItemsForCollision; 
    } 

    public bool Remove(T item) 
    { 
     bool removed = _BaseSet.Remove(item); 
     if (removed) 
      item.TestForCollision -= TestItemsForCollision; 
     return removed; 
    } 

    public void Clear() 
    { 
     foreach (var item in _BaseSet) 
      item.TestForCollision -= TestItemsForCollision; 
     _BaseSet.Clear(); 
    } 

    public void ExceptWith(IEnumerable<T> other) 
    { 
     throw new NotImplementedException(); 
    } 

    public void IntersectWith(IEnumerable<T> other) 
    { 
     throw new NotImplementedException(); 
    } 

    public void SymmetricExceptWith(IEnumerable<T> other) 
    { 
     throw new NotImplementedException(); 
    } 

    public void UnionWith(IEnumerable<T> other) 
    { 
     throw new NotImplementedException(); 
    } 

    //The rest of the functions will just be simply calling _BaseSet's version of the method. 
    public bool IsProperSubsetOf(IEnumerable<T> other) 
    { 
     return _BaseSet.IsProperSubsetOf(other); 
    } 
    //(snip) 
} 
+0

Je ne l'ai pas testé complètement, mais il semble correct. Merci. – Paparazzi

0

Je pense qu'un ObservableCollection (http://msdn.microsoft.com/en-us/library/ms668604.aspx) et un KeyedCollection (http://msdn.microsoft.com/en-us/library/ ms132440.aspx) serait un bon endroit pour commencer.

Vous pouvez créer un type personnalisé qui hérite de KeyedCollection et implémente l'interface INotifyCollectionChanged. En utilisant le KeyedCollection, vous pouvez "clé" sur la colonne ID, et cela renforcerait l'unicité de cela. Cependant, si vous voulez une vérification plus complexe, vous pouvez simplement implémenter un IList, INotifyCollectionChanged, et lancer des exceptions personnalisées si les critères ne sont pas remplis lors de l'ajout de nouveaux éléments.

+0

INotifyCollectionChanged Indique aux utilisateurs les modifications dynamiques, par exemple lorsque des éléments sont ajoutés et supprimés ou que la liste entière est actualisée. J'ai besoin de plus que d'ajouter des contrôles d'unicité. Si un élément est modifié, je veux arrêter le changement s'il n'est pas unique selon les critères de la question. Je pourrais passer la collection à l'article mais j'espérais qu'il y avait quelque chose de moins couplé. – Paparazzi

Questions connexes