我有一个组合框,其中包含一个值列表,后跟一个静态"添加新的"项目。当我选择该项时,它会加载图像并将图像的文件名添加到值列表中。但是,当我这样做时,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()
答案 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)
您可以使用ComboBox
和CompositeCollection
来添加新项目,而不是每次都从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;
}));
}
}
}