2009-06-12 14 views
3

Ich verfolge ListView-Auswahländerungen in einem MVVM-Design, indem ich an IsSelected binde. Ich muss das aktuelle Element auch verfolgen, indem ich IsSynchronizedWithCurrentItem aktiviere.MVVM: Bindung an List IsSelected beim Verfolgen IsSynchronizedWithCurrentItem

Ich finde, dass, wenn ich zwei Listview auf die gleiche Sammlung Bindung Ich erhalte die InvalidOperationException: „Sammlung modifiziert wurde; Enumerationsvorgang ausführen kann nicht“ Es scheint ein synchonization Fehler zwischen den beiden Listviews zu sein; einer löst ein PropertyChanged-Ereignis aus, während der andere den Selector vielleicht aktualisiert?

Ich kann nicht herausfinden, wie man anders als die Verwendung von IsSynchronizedWithCurrentItem verzichten und es selbst verwalten. Irgendwelche Ideen?

Danke.

Das Ansichtsmodell und Code hinter:

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(); 
    } 
} 

Die 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> 

Antwort

3

Ich kann keine direkte Lösung für Ihr Problem bieten. Ich habe jedoch eine Lösung, die funktioniert.

Sie können eine zweite Eigenschaft in Ihrem View-Modell namens 'SelectedItem' einführen, die einen Verweis auf das in Ihrer ListView ausgewählte Objekt enthält. Außerdem hören Sie in Ihrem View-Modell nach dem PropertyChanged-Ereignis. Wenn der zugeordnete Eigenschaftsname IsSelected ist, aktualisieren Sie die SelectedItem-Eigenschaft als Absender dieses Ereignisses (das Element, das jetzt IsSelected = true hat). Sie können dann die SelectedItem-Eigenschaft der ListView an die Eigenschaft desselben Namens der ViewModel-Klasse binden.

Mein Code für die überarbeitete ViewModel-Klasse ist unten.

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

Ja, tatsächlich bin ich gerade dabei, das Problem zu lösen. Ich würde hinzufügen, dass es für mich notwendig ist, das CollectionChanged-Ereignis bei Items zu überwachen, um die PropertyChanged-Registrierung hinzuzufügen oder zu entfernen, wenn sich die Sammlung ändert. – Terrence

+1

@terrence du solltest seine antwort annehmen, wenn das so funktioniert :) hast du im letzten jahr einen besseren weg gefunden? –

0

Das Problem scheint zu passieren, wenn Sie ein Listenfeld der binden IsSelected und verwenden SelectionMode='Single'

Ich fand, dass die SelectionMode = 'Multiple' ändert und dann einfach hinzugefügt Logik auf das Ansichtsmodell, um sicherzustellen, dass es immer nur ein Element war mit IsSelected wurde auf "Wahr" festgelegt.

Verwandte Themen