ComboBox错误:修改了集合,枚举操作可能无法执行

时间:2017-06-11 20:06:11

标签: c# wpf combobox

我有一个组合框,其中包含一个值列表,后跟一个静态"添加新的"项目。当我选择该项时,它会加载图像并将图像的文件名添加到值列表中。但是,当我这样做时,WPF底层代码会抛出"集合修改后的#34;异常。

XAML:

<StackPanel Orientation="Vertical">
    <ComboBox x:Name="selector">
        <ComboBoxItem IsEnabled="False" Content="---" />
        <ComboBoxItem FontStyle="Italic" Content="Add New" Selected="New_Selected" />
    </ComboBox>
</StackPanel>

代码:

public partial class MainWindow : Window
{
    List<string> files = new List<string>();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void RepopulateResourceSelector()
    {
        // Remove all but the bottom 2 items
        while (selector.Items.Count > 2)
        {
            selector.Items.RemoveAt(0);
        }

        int index = 0;

        // Add all strings in the list to combo box
        foreach (var file in files)
        {
            selector.Items.Insert(index, file);
            index++;
        }
    }

    private void New_Selected(object sender, RoutedEventArgs e)
    {
        var dlg = new OpenFileDialog();
        dlg.Filter = "Image Files (.bmp, .jpg, .gif, .png, .tiff)|*.bmp;*.jpg;*.gif;*.png;*.tiff";

        if (dlg.ShowDialog(this) == true)
        {
            // Add selected file to the list
            string name = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName);
            files.Add(name);

            RepopulateResourceSelector();
        }

        // Deselect `Add New` item
        selector.SelectedIndex = -1;
    }
}

堆栈追踪:

System.InvalidOperationException occurred
    HResult=0x80131509
    Message=Collection was modified; enumeration operation may not execute.
Source=<Cannot evaluate the exception source>
StackTrace:
    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
    at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
    at System.Collections.Generic.List`1.Enumerator.MoveNext()
    at System.Windows.Controls.Primitives.Selector.SelectionChanger.CreateDeltaSelectionChange(List`1 unselectedItems, List`1 selectedItems)
    at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
    at System.Windows.Controls.Primitives.Selector.SelectionChanger.SelectJustThisItem(ItemInfo info, Boolean assumeInItemsCollection)
    at System.Windows.Controls.ComboBox.NotifyComboBoxItemMouseUp(ComboBoxItem comboBoxItem)
    at System.Windows.Controls.ComboBoxItem.OnMouseLeftButtonUp(MouseButtonEventArgs e)
    at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
    at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
    at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
    at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
    at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
    at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
    at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
    at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
    at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
    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.RaiseTrustedEvent(RoutedEventArgs args)
    at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
    at System.Windows.Input.InputManager.ProcessStagingArea()
    at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
    at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
    at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
    at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
    at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
    at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
    at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
    at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
    at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
    at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
    at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
    at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
    at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
    at System.Windows.Application.RunDispatcher(Object ignore)
    at System.Windows.Application.RunInternal(Window window)
    at System.Windows.Application.Run(Window window)
    at System.Windows.Application.Run()
    at WpfApp1.App.Main()

2 个答案:

答案 0 :(得分:0)

您与RepopulateResourceSelector()存在同步问题,该异常告诉您正在迭代的Collection(这是一个Enumeration)正在同一个线程中同时被修改,这就是为什么要包装它使用Dispatcher.BeginInvoke()解决了您的问题(here is a detailed explanation on how it works),如果我是对的,也可以解决添加锁定问题,如下所示:

private Object filesLock = new Object();

private void RepopulateResourceSelector()
{
    // Remove all but the bottom 2 items
    while (selector.Items.Count > 2)
    {
        selector.Items.RemoveAt(0);
    }

    int index = 0;

    lock(filesLock){
        // Add all strings in the list to combo box
        foreach (var file in files)
        {
            selector.Items.Insert(index, file);
            index++;
        }
    }
}

答案 1 :(得分:0)

您可以使用ComboBoxCompositeCollection来添加新项目,而不是每次都从ObservableCollection删除项目。

试试这个:

<强> XAML:

<ComboBox x:Name="selector">
    <ComboBox.ItemsSource>
        <CompositeCollection>
            <CollectionContainer x:Name="cc" />
            <ComboBoxItem IsEnabled="False" Content="---" />
            <ComboBoxItem FontStyle="Italic" Content="Add New" Selected="New_Selected" />
        </CompositeCollection>
    </ComboBox.ItemsSource>
</ComboBox>

<强>代码:

public partial class MainWindow: Window
{
    ObservableCollection<string> files = new ObservableCollection<string>();

    public MainWindow()
    {
        InitializeComponent();
        cc.Collection = files;
    }

    private void New_Selected(object sender, RoutedEventArgs e)
    {
        var dlg = new OpenFileDialog();
        dlg.Filter = "Image Files (.bmp, .jpg, .gif, .png, .tiff)|*.bmp;*.jpg;*.gif;*.png;*.tiff";

        if (dlg.ShowDialog(this) == true)
        {
            // Add selected file to the list
            string name = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName);
            Dispatcher.BeginInvoke(new Action(() =>
            {
                files.Add(name);
                // Deselect `Add New` item
                selector.SelectedIndex = -1;
            }));
        }
    }
}