RoutedEvents gehören zum Visual- bzw. LogicalTree. Eventuell könnte man
da was machen, wenn man ein CustomControl selbst implementiert und die
Route für das eigene Event bestimmt. Damit kenne ich mich nicht aus und
vermutlich wäre die Lösung alles andere als optimal.
Wenn du UIElements in einer ObservableCollection(T) hast, läuft mit
Sicherheit was falsch. View und ViewModel sollten getrennt werden
(natürlich auch das Model - wo die Grenzen liegen, hängt von der
Anwendung ab). Dann hat man im ViewModel die View-repräsentierenden
Objekte, die INotifyPropertyChanged bzw. INotifyCollectionChanged
implementieren und die man per DataBinding an entsprechende in XAML
definierte View-Elemente bindet. Dabei helfen DataTemplates in
ItemsControls, die bestimmen, wie Items einer Collection dargestellt
werden.
Bleibt aber das Problem, dass man nicht mitbekommt, wenn sich ein Item
in der Collection ändert (wenn man das Problem im ViewModel lösen
möchte). Als Alternative bliebe die alte BindingList(T), deren
ListChanged-Event auch ausgelöst wird, wenn ein Item das
INotifyPropertyChanged.PropertyChanged feuert. Leider sind in den
EventArgs nur der alte und neue Index des geänderten Items hinterlegt,
was bei einem Remove problematisch ist - was soll man mit dem Index für
ein Item, das nicht mehr da ist? Ausserdem gibt es die BindingList(T)
nicht in Silverlight.
Wenn es wirklich notwendig ist, auf Änderungen der Items-Objekte zu
reagieren, würde ich wohl die ObservableCollection(T) in etwa wie folgt
erweitern:
1 | public class EnhancedCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
|
2 | {
|
3 | public event EventHandler<ItemChangedEventArgs<T>> ItemChanged;
|
4 | public EnhancedCollection()
|
5 | {
|
6 | CollectionChanged += (s, e) =>
|
7 | {
|
8 | if (e.OldItems != null) foreach (INotifyPropertyChanged oldItem in e.OldItems) oldItem.PropertyChanged -= item_PropertyChanged;
|
9 | if (e.NewItems != null) foreach (INotifyPropertyChanged newItem in e.NewItems) newItem.PropertyChanged += item_PropertyChanged;
|
10 | };
|
11 | }
|
12 | private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
13 | {
|
14 | if (ItemChanged != null) ItemChanged(this, new ItemChangedEventArgs<T>((T)sender, e.PropertyName));
|
15 | }
|
16 | }
|
17 |
|
18 | public class ItemChangedEventArgs<T> : EventArgs
|
19 | {
|
20 | public T Item { get; private set; }
|
21 | public string PropertyName { get; private set; }
|
22 |
|
23 | public ItemChangedEventArgs(T item, string propertyName)
|
24 | {
|
25 | Item = item;
|
26 | PropertyName = propertyName;
|
27 | }
|
28 | }
|
So kann man in der Klasse, die ein Objekt der EnhancedCollection(T)
referenziert auf die CollectionChanged- und ItemChanged-Events reagieren
und eventuell auch noch "weiter hinauf" (Im Objektmodell hat man anders
als in den WPF-Trees nicht zwingend eine Baustruktur!) triggern. In den
ItemChangedEventArgs hat man dann die Item-Referenz und den Namen des
Propertys, das sich geändert hat.
Wenn man das Ganze nur einmal braucht, könnte man sich die Ableitung
auch sparen und die Logik in die Klasse bauen, die ein Objekt der
ObservableCollection(T) referenziert.
Ein anderer Ansatz wäre, dem Datenobjekt im Konstruktor das Objekt
mitzugeben, das letztlich auf die Änderungen reagieren soll. Dann
bräuchte man die Collections gar nicht anfassen. :)