MVVM: привязка к списку IsSelected при отслеживании IsSynchronizedWithCurrentItem - PullRequest
3 голосов
/ 12 июня 2009

Я отслеживаю изменения выбора ListView в дизайне MVVM, связываясь с IsSelected. Мне также нужно отслеживать текущий элемент, включив IsSynchronizedWithCurrentItem.

Я обнаружил, что когда у меня есть две привязки ListView к одной и той же коллекции, я получаю InvalidOperationException : "Коллекция была изменена; операция перечисления может не выполняться. " Кажется, это ошибка синхронизации между двумя ListViews; один вызывает событие PropertyChanged, в то время как другой обновляет селектор, возможно?

Я не могу понять, как обойти это, кроме как отказаться от использования IsSynchronizedWithCurrentItem и управлять им самостоятельно. Есть идеи?

Спасибо.

ViewModel и код позади:

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

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>

Ответы [ 2 ]

4 голосов
/ 12 июня 2009

Я не могу предложить прямое решение вашей проблемы. Тем не менее, у меня есть решение, которое будет работать.

Что вы можете сделать, это ввести второе свойство в вашей модели представления, которое называется SelectedItem, которое будет содержать ссылку на элемент, выбранный в вашем ListView. Кроме того, в вашей View Model вы слушаете событие PropertyChanged. Если имя связанного свойства IsSelected, то вы обновляете свойство SelectedItem, чтобы быть отправителем этого события (элемент, который теперь имеет IsSelected = true). Затем вы можете привязать свойство SelectedItem ListView к свойству с тем же именем класса ViewModel.

Мой код для исправленного класса ViewModel приведен ниже.

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 голосов
/ 25 сентября 2015

Проблема возникает, когда вы связываетесь со списком IsSelected и используете SelectionMode='Single'

Я обнаружил, что изменение SelectionMode = 'Multiple', а затем просто добавил логику в ViewModel, чтобы убедиться, что когда-либо был только один элемент с IsSelected, установленным в true, работал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...