2009-06-12 4 views
3

Je suis le suivi des modifications de sélection ListView dans une conception MVVM en liant à IsSelected. J'ai également besoin de suivre l'élément actuel en activant IsSynchronizedWithCurrentItem.MVVM: Liaison à une liste IsSelected lors du suivi IsSynchronizedWithCurrentItem

Je trouve que lorsque j'ai deux ListView lier à la même collection j'obtenir le InvalidOperationException: «Collection a été modifiée, l'opération d'énumération ne peut pas exécuter » Il semble y avoir une erreur de synchonisation entre les deux listviews; l'un déclenche un événement PropertyChanged tandis que l'autre met à jour le Selector peut-être?

Je n'arrive pas à comprendre comment contourner cela en abandonnant l'utilisation de IsSynchronizedWithCurrentItem et en le gérant moi-même. Des idées?

Merci.

Le ViewModel et le code derrière:

public class Item : INotifyPropertyChanged 
{   
    public string Name{ get; set; } 

    public bool IsSelected 
    { 
     get { return isSelected; } 
     set { isSelected = value; OnPropertyChanged("IsSelected"); } 
    } 
    private bool isSelected; 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

public class ViewModel 
{ 
    public ViewModel() 
    { 
     Items = new ObservableCollection<Item>() 
       { 
        new Item(){Name = "Foo"}, 
        new Item(){Name = "Bar"} 
       }; 
    } 
    public ObservableCollection<Item> Items { get; private set; } 
} 

public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     InitializeComponent(); 
     DataContext = new ViewModel(); 
    } 
} 

Le XAML:

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="100" Width="100"> 
    <StackPanel> 
     <ListView DataContext="{Binding Items}" ItemsSource="{Binding}" 
        IsSynchronizedWithCurrentItem="True" SelectionMode="Single"> 
      <ListView.ItemContainerStyle> 
       <Style TargetType="ListViewItem"> 
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> 
       </Style> 
      </ListView.ItemContainerStyle> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Path=Name, Mode=OneWay}"/> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
     </ListView> 
     <ListView DataContext="{Binding Items}" ItemsSource="{Binding}" 
       IsSynchronizedWithCurrentItem="True" SelectionMode="Single"> 
      <ListView.ItemContainerStyle> 
       <Style TargetType="ListViewItem"> 
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> 
       </Style> 
      </ListView.ItemContainerStyle> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Path=Name, Mode=OneWay}"/> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
     </ListView> 
    </StackPanel> 
</Window> 

Répondre

3

Je ne peux pas offrir une solution directe pour votre problème. Cependant, j'ai une solution qui fonctionnera. Ce que vous pouvez faire est d'introduire une deuxième propriété appelée «SelectedItem» dans votre View Model qui contiendra une référence à l'élément sélectionné dans votre ListView. En outre, dans votre modèle de vue, vous écoutez l'événement PropertyChanged. Si le nom de propriété associé est IsSelected, vous mettez à jour la propriété SelectedItem pour qu'elle soit l'expéditeur de cet événement (l'élément qui a maintenant IsSelected = true). Vous pouvez ensuite lier la propriété SelectedItem de ListView à la propriété du même nom de la classe ViewModel.

Mon code pour la classe ViewModel révisée est ci-dessous.

public class ViewModel : INotifyPropertyChanged 
{ 
    private Item _selectedItem; 

    public ViewModel() 
    { 
     Items = new ObservableCollection<Item>() 
      { 
       new Item {Name = "Foo"}, 
       new Item {Name = "Bar"} 
      }; 

     foreach (Item anItem in Items) 
     { 
      anItem.PropertyChanged += OnItemIsSelectedChanged; 
     } 
    } 

    public ObservableCollection<Item> Items { get; private set; } 

    public Item SelectedItem 
    { 
     get { return _selectedItem; } 
     set 
     { 
      // only update if the value is difference, don't 
      // want to send false positives 
      if (_selectedItem == value) 
      { 
       return; 
      } 

      _selectedItem = value; 
      OnPropertyChanged("SelectedItem"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnItemIsSelectedChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName != "IsSelected") 
     { 
      return; 
     } 

     SelectedItem = sender as Item; 
    } 

    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 
+0

Oui, en effet, c'est la façon dont je travaille sur le problème pour le moment. J'ajouterais que pour moi, il est nécessaire de surveiller l'événement CollectionChanged sur Items pour ajouter/supprimer l'enregistrement de PropertyChanged au fur et à mesure que la collection change. – Terrence

+1

@terrence vous devriez accepter sa réponse si c'est ainsi que cela fonctionne :) avez-vous trouvé une meilleure solution l'année dernière? –

0

Le problème semble se produire lorsque vous liez à un IsSelected de listbox et utiliser SelectionMode='Single'

Je trouve que le changement de SelectionMode = 'Multiple' et logique vient d'ajouter à la ViewModel pour assurer qu'il n'y avait jamais qu'un seul élément avec IsSelected est défini sur true travaillé.

Questions connexes