Bonjour,
Toujours dans mon test de DataTemplate pour ListBox.
La hierarchie est la suivante :
le Model : un objet Person qui contient 2-3 propriétés String et une collection d'objet Phone qui contient 2 propriétés String.
Class Person :
Class Phone:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 Public Property Name As String Public Property SubName As String Public Property Age As Byte Public Property ID As Integer Public Property Phones As Observable(Of Phone)
Une View principale qui contient une ListBox bindée sur une observable de Person,
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Public Property PhoneNumber As String Public Property PhoneType As String
avec un DataTemplate
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 <Grid.DataContext> <Binding Source="{StaticResource MainWindowViewModelDataSource}"/> </Grid.DataContext> <ListBox x:Name="lbox" Margin="5" Grid.Row="0" ItemsSource="{Binding PersonList}">
composé d'un UserControl (générique permettant de déplacer copier supprimer remplacer etc un item) contenant un autre UserControl (pour afficher l'objet Person),
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 <DataTemplate> <local:PersonMoveableItem> <i:Interaction.Triggers> <i:EventTrigger EventName="DeleteItem"> <ei:CallMethodAction MethodName="HandleDeleteItem" TargetObject="{Binding ElementName=lbox, Path=DataContext}"/> </i:EventTrigger> </i:Interaction.Triggers> </local:PersonMoveableItem> </DataTemplate> </ListBox.ItemTemplate>
lequel lui-même a une listbox dont le template est similaire et affiche un objet Phone.
l'item PersonMoveable:
View PersonTemplate :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 Public Class PersonMoveableItem Inherits MoveableItem Sub New() MyBase.New() MyBase.ItemObject = New PersonTemplate End Sub End Class
L'item PhoneMoveable
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <StackPanel HorizontalAlignment="Left"> <StackPanel Orientation="Horizontal" > <TextBlock Text="{Binding Name}" MinWidth="50" Margin="5"/> <TextBlock Text="{Binding SubName}" MinWidth="50" Margin="5"/> <TextBlock Text="{Binding Age}" MinWidth="50" Margin="5"/> <TextBlock Text="{Binding ID}" MinWidth="50" Margin="5"/> </StackPanel> <ListBox ItemsSource="{Binding Phones}"> <ListBox.ItemTemplate> <DataTemplate> <local:PhoneMoveableItem> </local:PhoneMoveableItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel>
le Template Phone :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 Public Class PhoneMoveableItem Inherits MoveableItem Sub New() MyBase.New() MyBase.ItemObject = New PhoneTemplate End Sub End Class
Un ViewModel qui me fournit la collection observable de Person et les Handle pour les routedEvent du UserControl me permettant de supprimer/ajouter etc un objet Person ou Phone.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 <StackPanel Orientation="Horizontal" x:Name="LayoutRoot"> <TextBlock MinWidth="50" Text="{Binding PhoneType}" Margin="2"/> <TextBlock MinWidth="50" Text="{Binding PhoneNumber}"/> </StackPanel>
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 Private _personList As ObservableCollection(Of Person) Public Property PersonList() As ObservableCollection(Of Person) Get If IsNothing(_personList) Then _personList = GetPersonList() End If Return _personList End Get Set(ByVal value As ObservableCollection(Of Person)) _personList = value OnPropertyChanged(Me, "PersonList") End Set End PropertyTant que ma collection de Phone est déclarée Observable dans la classe Person, tout fonctionne correctement, parfait.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 Public Sub HandleDeleteItem(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Select Case e.OriginalSource.GetType //Person deletion Case GetType(PersonMoveableItem) Dim p As PersonMoveableItem = DirectCast(e.OriginalSource, PersonMoveableItem) If Me.PersonList.Count > 1 Then Me.PersonList.Remove(p.DataContext) Else Me.PersonList(0) = New Person End If //phone deletion Case GetType(PhoneMoveableItem) Dim s As ContentPresenter = e.Source Dim p As PersonMoveableItem = s.TemplatedParent Dim d As Person = p.DataContext Dim phoneitem As PhoneMoveableItem = e.OriginalSource d.Phones.Remove(phoneitem.DataContext) End Select e.Handled = True End Sub
Mais, un peu neuf dans le MVVM, je me dis que ce n'est pas très rigoureux d'avoir un Observable dans le Model !!
Derechef, je transforme la propriété Phones dans Person en List(Of Phone) et je créé un Converter ListToObservable pour mon UserControl qui affiche la collection de Phone d'une Person.
Mais, bim badaboum, le converter instancie une New observable à partir de la List(Of), donc lorsque je récupère l'évènement Delete par exemple, je supprime un élément de la liste de Phone de l'objet Person affiché, mais je supprime pas l'élément de collection observable créée et, évidemment, l'élément reste dans la liste !!
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 Public Class ListToObservableConverter Implements IValueConverter //source to target Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert If IsNothing(value) Then Return value End If Dim myType As Type = value.GetType().GetProperty("Item").PropertyType Dim myList As IEnumerable(Of Object) = value Return New ObservableCollection(Of Object)(myList) End Function //target to source Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack Throw New NotImplementedException End Function End Class
Donc, ma question est la suivante : comment respecter le modèle MVVM sans 'casser' le lien entre la collection Phone affichée et ma collection Person.Phone accessible via le ViewModel ?
Merci,
Stéphane.
Partager