EF et WPF
Suite à une question que j'ai reçu par mail, j'ai développé ma première application WPF. 
Voici la question :
"J'utilise EF afin d'obtenir une extraction objet de ma BDD. Pour simplifier disons que j'ai une base COllège, avec une table Classe et une table élève.
Les élèves ne pouvant appartenir qu' à une Classe. J'ai crée en WPF, 2 fenêtre pour ajouter des classes et ajouter des élèves.
Dans la fenêtre des élèves j'ai une combo qui affiche les classes disponibles. Et je souhaitais que les items de cette combo, soient binder automatiquement à mon objet CollegeEntities. Hélas CollegeEntities.Classes ne dérive pas de ObservableCollection<T> et du coup lorsque je créé via ma fenêtre d'édition une nouvelle Classes, mon objet Combo n'est pas mise à jour.
Est-ce possible aujourd'hui avec EF de DataBinder automatiquement les listes d'Entities généré par EF ?"
Le premier point est le fait que les collections d'entitésObjectQuery<T> n'implémente pas INotifyCollectionChanged (en effet c'est cette interface qui permet à un contrôle dont la DataSource est un ObservableCollection<T> de se raffraîchir).
De plus, l'ObjectQuery ne renverra pas les nouvelles Classes tant que celles-ci ne seront pas persistées en base avec le Context.SaveChanges().
Donc comment faire ?
Tout simplement passé par une classe intermédiaire :
public class ContextEntitiesLoaded<T> : IEnumerable<T>, INotifyCollectionChanged
{
private ObjectContext _context;
private EntityState _states = EntityState.Added | EntityState.Modified | EntityState.Unchanged;
public ContextEntitiesLoaded(ObjectContext context)
{
_context = context;
_context.ObjectStateManager.ObjectStateManagerChanged += (sender, e) =>
{
if (e.Element is T)
switch (e.Action)
{
case CollectionChangeAction.Add:
case CollectionChangeAction.Remove:
if (e.Element is T)
OnNotifyCollectionChanged();
break;
}
};
}
public EntityState States
{
get { return _states; }
set { _states = value; }
}
public Func<T, object> SortExpression { get; set; }
protected virtual void OnNotifyCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
var q = _context.ObjectStateManager.GetObjectStateEntries(States).Select(se => se.Entity).OfType<T>();
if (SortExpression != null)
q = q.OrderBy(SortExpression);
return q.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
}
Ensuite, il suffit de récupérer les classes dans le context et de le binder avec la combo :
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ObjectDataProvider odp = this.FindResource("maSource") as ObjectDataProvider;
if (odp != null)
{
foreach (var c in NorthwindEntities.Instance.Categories); // to load the categories on the context
odp.ObjectInstance = new ContextEntitiesLoaded<Category>(NorthwindEntities.Instance) { SortExpression = c => c.CategoryName };
}
}
[...]
}
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 :