重新设置 WPF ListView 的 ItemsSource 后,它抛出了一个 ArgumentException。

3

我的应用程序中有一个ListView,目前其中有2个项目。

<ListView Name="lstViewFolderSettings" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="0" SelectionMode="Single" SelectionChanged="lstViewFolderSettings_SelectionChanged">
    <ListView.View>
        <GridView>
            <GridViewColumn Width="100" Header="Type" DisplayMemberBinding="{Binding Name}"  />
            <GridViewColumn Width="250" Header="Folder" DisplayMemberBinding="{Binding FolderPath}" />
            <GridViewColumn Width="350" Header="XPath" DisplayMemberBinding="{Binding XPath}" />
        </GridView>
    </ListView.View>
</ListView>

然后我会按照以下方式设置我的ItemsSource

lstViewFolderSettings.ItemsSource = fileSeperationSettings.FileSettings;

SelectionChanged 事件中,我获取选定的项并填充一些控件。然后我点击保存,更新我的集合并重新设置 ItemsSource
lstViewFolderSettings.ItemsSource = null;
lstViewFolderSettings.ItemsSource = fileSeperationSettings.FileSettings;

我必须先将其设置为null,否则ListView在视图中不会更新。
这一切似乎都很好,直到我在同一项上两次更改我的选择。
即:选择项目1->更改->更新,选择项目2,选择项目1,选择项目2->BANG!
我所指的BANG!是 ArgumentException未处理 已经添加了具有相同键的项。 堆栈跟踪:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2..ctor(IDictionary`2 dictionary, IEqualityComparer`1 comparer)
at System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage collection, IEqualityComparer`1 equalityComparer)
at System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple()
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
at System.Windows.Controls.Primitives.Selector.SetSelectedHelper(Object item, FrameworkElement UI, Boolean selected)
at System.Windows.Controls.Primitives.Selector.NotifyIsSelectedChanged(FrameworkElement container, Boolean selected, RoutedEventArgs e)
at System.Windows.Controls.Primitives.Selector.OnSelected(Object sender, RoutedEventArgs e)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
at System.Windows.Controls.ListBoxItem.OnSelected(RoutedEventArgs e)

--- 更新 --- SelectionChanged事件处理程序代码。

private void lstViewFolderSettings_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    m_SelectedSetting = lstViewFolderSettings.SelectedItem as FileSetting;
    txtFolder.Text = m_SelectedSetting.FolderPath;
    txtType.Text = m_SelectedSetting.Name;
    txtXPath.Text = m_SelectedSetting.XPath;

     e.Handled = true;
}

-- 更新 ----

现在我有了这个:

ObservableCollection<FileSetting> _fileSettings;
public ObservableCollection<FileSetting> FileSettings
{
    get 
    {
        if (_fileSettings == null)
  {
            FileSeperationSettings fileSeperationSettings = m_config.GetSection("fileSeperationSettings") as FileSeperationSettings;

            _fileSettings = new ObservableCollection<FileSetting>(fileSeperationSettings.FileSettings.Cast<FileSetting>());
        }

        return _fileSettings;
    }
}

我向这个集合中添加和删除元素

FileSettings.Add(fsSetting);
FileSettings.Remove(fsSetting);

我获取了所选项目

m_SelectedSetting = lstViewFolderSettings.SelectedItem as FileSetting;

txtFolder.Text = m_SelectedSetting.FolderPath;
txtType.Text = m_SelectedSetting.Name;
txtXPath.Text = m_SelectedSetting.XPath;

我更新了该项。
FileSetting fs = FileSettings.First(x => x.Name == m_SelectedSetting.Name);
fs.Name = txtType.Text;
fs.FolderPath = txtFolder.Text;
fs.XPath = txtXPath.Text;

在我进行更新并第二次更改所选项目后,出现了错误...


你需要发布完整的异常信息,包括堆栈跟踪... - Neil Barnwell
为什么每次都要更新ItemsSource?如果该列表中的对象实现了INotifyPropertyChanged,则可以更改它们,WPF数据绑定将自动更新UI。您真正需要做的是只设置一次这些内容。 - Neil Barnwell
1
事实上,如果可以的话,最好使用数据绑定来设置源,而不是通过代码后台来实现。 - Neil Barnwell
不,恰恰相反 - 正如我在第二条评论中所说的那样 - 您应该仅设置一次ItemsSource,为ObserableCollection<T>或类似类型,其中每个项都实现了INotifyPropertyChanged。一旦所有这些都设置好了(使用空列表),并绑定到listview,您就可以添加项目,编辑它们,删除它们等等,数据绑定将自动更新UI。 - Neil Barnwell
@NeilBarnwell,我不理解的是为什么我被迫使用实现了INotifyPropertyChanged接口的集合……我为什么不能只设置一个数据源就行了呢? - Squirrel5853
显示剩余12条评论
2个回答

1

我通过阅读以下内容无法清除WPF ListBox.SelectedItems集合来解决了这个问题,我意识到无法移除我的选择项的原因是它在集合中不存在(HashCode已更改),或者像这样的一些疯狂的东西...

所以我将SelectionChanged事件更改为以下内容

FileSetting selectedItem;
private void lstViewFolderSettings_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    selectedItem = lstViewFolderSettings.SelectedItem as FileSetting;
    txtFolder.Text = selectedItem.FolderPath;
    txtType.Text = selectedItem.Name;
    txtXPath.Text = selectedItem.XPath;

    lstViewFolderSettings.UnselectAll();
}

所以现在我自己跟踪所选项目。这意味着我可以随心所欲地设置和重新设置ItemsSource。

这并不是“疯狂”的行为——哈希码的整个目的就是用于哈希表,以便快速定位哈希表中的对象。对象的哈希码必须是一个不可变的属性,在对象构造后不能更改。当我安装了.NET 4.5时,遇到了相同的错误,因为它现在使用哈希表来选择项目。 - Jim Balter

0

我会避开 Code-Behind,更多地利用数据绑定。

你的集合应该是 UI 的 DataContext 属性:

public class MyViewModelOrCodeBehindClass
{
    public FileSetting SelectedItem { get; set; }

    public ObservableCollection<FileSetting> FileSettings 
    {
        get; 
        private set; 
    }

    public MyViewModel()
    {
        FileSettings = new ObservableCollection<FileSetting>();
        // If you're using codebehind rather than having something 
        // else set this class as the datacontext:
        DataContext = this;
    }
}

在您看来:

<ListView ItemsSource="{Binding FileSettings}" SelectedItem="{Binding SelectedItem}" />
<TextBlock Text="{Binding SelectedItem.Folder}" />
<TextBlock Text="{Binding SelectedItem.Name}" />
<TextBlock Text="{Binding SelectedItem.XPath}" />

在您的代码后台/视图模型中,您只需添加/删除项目,数据绑定就会完成其余工作。更好的是,如果列表中的每个项目都实现了INotifyPropertyChanged,那么对这些项目的编辑也将自动更新到UI中。您真的不需要处理SelectionChanged

1
我以前遇到过这个错误,而且我在所有的UI控件上都使用数据绑定,所以我不确定这是否是解决这个问题的方法。尽管如此,我仍然建议用户通常使用这种绑定方法。 - Sheridan
仍然出现相同的错误,按照上面提供的步骤进行操作...此外我的用户界面没有自动处理。我发现我不得不使用 CollectionViewSource.GetDefaultView(lstViewFolderSettings).Refresh(); - Squirrel5853
@NeilBarnwell错过了ListViewDataContext设置,现在绑定时不需要我在codeBehind中显式设置它...但是我仍然遇到了一些主要问题...肯定不应该这么难设置ListView的数据源吧? - Squirrel5853
@NeilBarnwell 我现在已经删除了所有关于ItemsSource的更新,而是使用绑定到ListView的类的集合属性进行操作,但是这并没有按照预期工作。 - Squirrel5853
这并不是真的难。只是没有全面了解所有内容如何配合才能看出是什么导致了错误。事实上,你不应该将ItemsSource设置为null,然后再重新设置回去。如果不进行这个调用,错误是否会消失?你能否发布一个最小化的示例代码片段或其他东西来重现错误,以便我可以查看? - Neil Barnwell
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接