xceedsoftware / wpftoolkit

All the controls missing in WPF. Over 1 million downloads.
Other
3.87k stars 872 forks source link

CollectionControl does not handle arrays properly #814

Open xceedsoftware opened 7 years ago

xceedsoftware commented 7 years ago

arobincaron[CodePlex]
Item 20216 raised some issues with handling fixed size collection (e.g. arrays). Some changes were made to resolve issues.

Unfortunately there are still issues. Take the following class as the selected object class: public class Person { [Category(quotPersonal Informationquot)] [DisplayName(@quotFirst Namequot)] [Description(quotThe first name or given name.quot)] public string FirstName { get; set; }

[Category(quotPersonal Informationquot)]
[DisplayName(@quotLast Namequot)]
[Description(quotThe last name, family name or surnamequot)]
public string LastName { get; set; }

}

public class MarriedPerson : Person { [Category(quotMarriage Informationquot)] [Description(quotThe spouse personal information.quot)] [ExpandableObject] public Person Spouse { get; set; }

[Category(quotMarriage Informationquot)]
[Description(quotThe children personal information.quot)]
public Person[] Children { get; set; }

}

Editing the Children member creates a CollectionControl. If you add some items and then click OK you get the null reference exception:

at Xceed.Wpf.Toolkit.CollectionControl.CreateItemsSource() at Xceed.Wpf.Toolkit.CollectionControl.ComputeItemsSource() at Xceed.Wpf.Toolkit.CollectionControl.PersistChanges() at Xceed.Wpf.Toolkit.CollectionControlDialog.OkButton_Click(Object sender, RoutedEventArgs e) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.Controls.Button.OnClick() at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 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.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 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 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, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSGamp msg) at System.Windows.Threading.Dispatcher.TranslateAndDispatchMessage(MSGamp msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Window.ShowHelper(Object booleanBox) at System.Windows.Window.Show() at System.Windows.Window.ShowDialog() at Xceed.Wpf.Toolkit.PropertyGrid.Editors.CollectionEditor.Button_Click(Object sender, RoutedEventArgs e) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.Controls.Button.OnClick() at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 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.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) 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 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, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Booleanamp handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSGamp msg) at System.Windows.Threading.Dispatcher.TranslateAndDispatchMessage(MSGamp msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Application.RunInternal(Window window) at TradeLifeCycleMessageSample.Program.Main(String[] args) in c:\Users\U319813\Documents\Visual Studio 2012\Projects\TradeLifeCycleMessageSample\TradeLifeCycleMessageSample\Program.cs:line 20 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

I have corrected this exception by properly handling creation of arrays in ComputeItemsSource. With that fixed PersistChanges throws because the Items collection is modified after creating the ItemsSource. This occurs because of inconsistent handling of changes in the OnItemSourceChanged method. Correcting this requires changing how the ItemsSource is set otherwise it is not possible to eliminate the changing Items collection after setting up the new ItemsSource and copying items out of it.

Here are the changes I am suggesting to correct these issues:

Change OnItemsSourceChanged to: public void OnItemSourceChanged(IList oldValue, IList newValue) { Items.Clear();

if (newValue != null) { foreach (var item in newValue) Items.Add(item); } }

Change CreateItemsSource to: private IList CreateItemsSource() { IList list = null;

if (ItemsSourceType != null) { ConstructorInfo constructor = ItemsSourceType.GetConstructor(new[] { typeof(int) }); if (constructor == null) { constructor = ItemsSourceType.GetConstructor(Type.EmptyTypes); if (constructor == null) { throw new Exception(quotNo usable constructor to build collectionquot); }

Change PersistChanges to: public void PersistChanges() { IList list = CreateItemsSource(); if (list == null) return;

//the easiest way to persist changes to the source is to just clear the source list and then add all items to it. list.Clear();

if (list.IsFixedSize) { for (int i = 0; i lt Items.Count; ++i) list[i] = Items[i]; } else { foreach (var item in Items) { list.Add(item); } }

ItemsSource = list; }

and delete ComputeItemsSource.

xceedsoftware commented 7 years ago

BoucherS[CodePlex]
Hi,

Correction, v2.3 will fix this issue. Users of the Plus Edition already have v2.3. You should have the fix in the next version, unless you go with the Plus Edition.

xceedsoftware commented 7 years ago

arobincaron[CodePlex]
Thanks for letting me know. I would like that I can get back to a standard/stable build as soon as possible. Is it possible to see/test this code before the actual release?

xceedsoftware commented 7 years ago

BoucherS[CodePlex]
Hi,

This is fixed in v2.4.