c# - MVVM:在跟踪 IsSynchronizedWithCurrentItem 时绑定(bind)到列表 IsSelected

标签 c# wpf listview mvvm

我通过绑定(bind)到 IsSelected 来跟踪 MVVM 设计中的 ListView 选择更改。我还需要通过启用 IsSynchronizedWithCurrentItem 来跟踪当前项目。

我发现当我有两个 ListView 绑定(bind)到同一个集合时,我得到了 InvalidOperationException:“Collection was modified; enumeration operation may not execute.”似乎是两个 ListView 之间的同步错误;一个正在触发 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>

最佳答案

我无法直接解决您的问题。不过,我确实有一个可行的解决方案。

您可以做的是在您的 View 模型中引入第二个名为“SelectedItem”的属性,该属性将包含对在您的 ListView 中选择的项目的引用。此外,在您的 View 模型中,您会监听 PropertyChanged 事件。如果关联的属性名称是 IsSelected,则将 SelectedItem 属性更新为该事件的发送者(现在 IsSelected = true 的 Item)。然后,您可以将 ListView 的 SelectedItem 属性绑定(bind)到 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));
        }
    }
}

关于c# - MVVM:在跟踪 IsSynchronizedWithCurrentItem 时绑定(bind)到列表 IsSelected,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/985868/

相关文章:

c# - 为什么 C# 不允许静态方法实现接口(interface)?

c# Visual Studio 似乎遵循了错误的代码路径

wpf - 将文件从资源管理器拖放到 Telerik WPF TreeView 上

wpf - VS2012 - 将 WPF 现有用户控件添加到项目

android - ListView 崩溃应用程序 :Java null Pointer Exception

c# - 链式构造函数中自定义对象的默认值

c# - Javascript 从 C# 方法获取进度

wpf - ControlTemplate 或 DataTemplate 中的自定义资源字典

android - 如何在不同的线程中生成列表并填充到 ListView 中?

java.lang.IllegalArgumentException :Duplicate id 0x7f0d00c0, 标记为空,带有 com.google.android.gms.maps.SupportMapFragment 的另一个 fragment