Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

EnumerableCollection

Prenons le scénario suivant. On utilise MVVM.

On a les deux classes suivantes dans le model :

public class Child
{
}
public class Parent
{
    private ObservableCollection<Child> _children;
    public ObservableCollection<Child> Children
    {
        get { return _children ?? (_children = new ObservableCollection<Child>()); }
    }
}
et les deux suivantes dans le ViewModel :
public class ChildViewModel
{
    public ChildViewModel(Child child)
    {
        Child = child;
    }

    private Child Child { get; set; }
}
public class ParentViewModel 
{
    public ParentViewModel(Parent parent = null)
    {
        Model = parent ?? new Parent();
    }
    private Parent Model { get; set; }
    private ObservableCollection<ChildViewModel> _children;
    public ObservableCollection<ChildViewModel> Children
    {
        get { return _children ?? (_children = new ObservableCollection<ChildViewModel>(Model.Children.Select(c => new ChildViewModel(c)))); }
    }
}
Comment gérer le problème de synchronisation entre les deux collections ?
Je pense que, dans ce cas, le ViewModel ne devrait pas exposer une collection mais seulement un IEnumerable.
Mon code deviendrait alors ceci :
public class ParentViewModel : INotifyPropertyChanged
{
    public ParentViewModel(Parent parent = null)
    {
        Model = parent ?? new Parent();
        Model.Children.CollectionChanged += delegate { RaisePropertyChanged(() => Children); };
    }
    private Parent Model { get; set; }
    public IEnumerable<ChildViewModel> Children
    {
        get { return Model.Children.Select(c => new ChildViewModel(c)); }
    }
    #region INotifyPropertyChanged Members
    public void RaisePropertyChanged<T>(Expression<Func<T>> exp)
    {
        var memberExpression = exp.Body as MemberExpression;
        if (memberExpression == null)
            return;
        string propertyName = memberExpression.Member.Name;
        if (propertyName != null && PropertyChanged != null)       
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion
}
Ca marche. Cependant, si j’ajoute ou supprime un child de la collection Parent.Children, tous mes ChildViewModel sont recréés se qui peut poser un gros problème de binding.
Pour résumer, on a besoin d’un IEnumerable<T> qui implémente aussi INotifyCollectionChanged.
Je propose donc la classe EnumerableCollection suivante :
public interface IEnumerableCollection<out T> : IEnumerable<T>, INotifyCollectionChanged
{
    void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e);
}

public class EnumerableCollection<T> : EnumerableCollection<T, T>
{
    public EnumerableCollection(ObservableCollection<T> observableCollection)
        : base(observableCollection, null)
    {
    }

    public EnumerableCollection(IEnumerable<ObservableCollection<T>> observableCollections)
        : base(observableCollections, null)
    {
    }

    public EnumerableCollection(params ObservableCollection<T>[] observableCollections)
        : this((IEnumerable<ObservableCollection<T>>)observableCollections)
    {
    }

    public EnumerableCollection(IEnumerable<IEnumerableCollection<T>> enumerableCollections)
        : base(enumerableCollections, null)
    {
    }

    public EnumerableCollection(params IEnumerableCollection<T>[] observableCollections)
        : this((IEnumerable<IEnumerableCollection<T>>)observableCollections)
    {
    }

    public EnumerableCollection(IEnumerable<T> enumerable)
        : base(enumerable, null)
    {
    }
}

public class EnumerableCollection<TSource, TResult> : IEnumerableCollection<TResult>, IEnumerable<TResult>, INotifyCollectionChanged
{
    private IEnumerable<TResult> _enumerable;

    #region ctors
    public EnumerableCollection(ObservableCollection<TSource> observableCollection, Func<TSource, TResult> selector)
    {
        observableCollection.CollectionChanged += (sender, e) => RaiseCollectionChanged(e);
        _enumerable = Convert(observableCollection, selector);
    }

    public EnumerableCollection(IEnumerable<ObservableCollection<TSource>> observableCollections, Func<TSource, TResult> selector)
    {
        foreach (ObservableCollection<TSource> observableCollection in observableCollections)
            observableCollection.CollectionChanged += (sender, e) => RaiseCollectionChanged(e);
        _enumerable = Convert(observableCollections.SelectMany(item => item), selector);
    }

    public EnumerableCollection(IEnumerable<IEnumerableCollection<TSource>> enumerableCollections, Func<TSource, TResult> selector)
    {
        foreach (EnumerableCollection<TSource> enumerableCollection in enumerableCollections)
            enumerableCollection.CollectionChanged += (sender, e) => RaiseCollectionChanged(e);
        _enumerable = Convert(enumerableCollections.SelectMany(item => item), selector);
    }

    public EnumerableCollection(IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
    {
        _enumerable = Convert(enumerable, selector);
    }
    #endregion ctors

    private IEnumerable<TResult> Convert(IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        if (selector != null)
            return source.Select(selector);
        if (typeof(TSource) == typeof(TResult))
            return (IEnumerable<TResult>)source;
        throw new InvalidOperationException();
    }

    public void OrderBy<K>(Func<TResult, K> keySelector)
    {
        if (_enumerable == null)
            throw new InvalidOperationException();
        _enumerable = _enumerable.OrderBy(keySelector);
    }

    public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, e);
    }

    #region IEnumerable<T> Members
    public IEnumerator<TResult> GetEnumerator()
    {
        return _enumerable.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion

    #region INotifyCollectionChanged Members
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    #endregion
}
Avec ceci, le code de mon ParentViewModel devient ceci :
public class ParentViewModel
{
    public ParentViewModel(Parent parent = null)
    {
        Model = parent ?? new Parent();
    }

    private Parent Model { get; set; }

    private EnumerableCollection<Child, ChildViewModel> _children;
    public EnumerableCollection<Child, ChildViewModel> Children
    {
        get { return _children ?? (_children = new EnumerableCollection<Child, ChildViewModel>(Model.Children, c => new ChildViewModel(c))); }
    }
}
Hope that helps
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié vendredi 3 septembre 2010 23:23 par Matthieu MEZIL

Classé sous : , , ,

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01