WPF DataGrid SortMemberPath for ArrayList data type

I have a WPF DataGrid with a column based on an ArrayList with zero to many strings. I have a type converter that applies some logic to handle zero, one or many and return a single string. This is all working just great.

However, attempting to sort the grid on this column raises an exception. The solution is to assign a SortMemberPath but nothing that I've tried so far has any useful effect.

In the examples below, "Types" is the name of the property containing an ArrayList.
Public Class TypeListToStringConverter
  Implements IValueConverter

  Public Function Convert(...) As Object Implements ...
    Dim list As ArrayList = CType(value, ArrayList)
    If list.Count = 0 Then
      Return "Uknown"
    ElseIf list.Count = 1 Then
      Return list(0).ToString
    Else
      Return "Multi (" & list.Count.ToString & ")"
    End If
  End Function
  ...
End Class

Open in new window

<DataGridTextColumn 
  Header="Type(s)" 
  Binding="{Binding Path=Types, Converter={StaticResource TypeListToStringConverter}}" 
  SortMemberPath="Types"
/>

Open in new window

LVL 11
Craig YellickDatabase ArchitectAsked:
Who is Participating?
 
Bob LearnedCommented:
Research boxing and unboxing, which is a process where value types are converted to reference types to store in an ArrayList.  This is a fairly heavy process that is avoid if you use List

Boxing and Unboxing for Beginners
http://codebetter.com/raymondlewallen/2005/07/29/boxing-and-unboxing-for-beginners/

Understand that the generics list doesn't require boxing and unboxing.

Boxing Issues and Strongly Typed Collections
http://en.csharp-online.net/Understanding_Generics%E2%80%94Boxing_Issues_and_Strongly_Typed_Collections
0
 
Bob LearnedCommented:
ArrayList is an old-school approach to lists.  I much prefer the strong-typed generics List(Of) approach.  I don't see the exception that you are getting.
0
 
Craig YellickDatabase ArchitectAuthor Commented:
I should have been more clear.  I get the exception when SortMemberPath is not specified. The exception goes away when SortMemberPath is set, but no paths that I've tried actually yield a useful sort.

I used ArrayList with the possibly mistaken assumption that it is a light yet functional way to store small lists. I wanted more features than a pure array but didn't need more than ArrayList provides.
0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
Bob LearnedCommented:
ArrayList stores objects, which are evaluated at run time, instead of List(Of), which can be evaluated at compile time, so they are a much better approach to lists.  Generics lists are light-weight, so don't be afraid to use them.

Can you show me a snippet of your XAML, so that I might get a better picture of what you are working with?

0
 
Craig YellickDatabase ArchitectAuthor Commented:
The DataGrid context is shown below, along with the relevant code that sets the datagrid's ItemsSource. The source code for the converter is above in the original post.

I will convert ArrayList to List-Of but I can't see how that could have any effect. I did some research on pros/cons of ArrayList versus other options and couldn't find anything definitive about memory or processing advantages. Strongly-type'd objects are better at design time, I agree, so I'll make the change on those merits.
<DataGrid Name="SearchResultsDataGrid"  AutoGenerateColumns="False">
  <DataGrid.Columns>
    <DataGridTextColumn Header="Type(s)" Binding="{Binding Path=Types, Converter={StaticResource TypeListToStringConverter}}" />
    <DataGridTextColumn Header="Scope" Binding="{Binding Path=Scope}" />
    <DataGridTextColumn Header="Folder" Binding="{Binding Path=Folder}" />
    <DataGridTextColumn Header="Title"Binding="{Binding Path=Title}"/>
    <DataGridTextColumn Header="Description" Binding="{Binding Path=Description}" />
    <DataGridTextColumn Header="Type Names" Binding="{Binding Path=TypeNames}" />
    <DataGridTextColumn Header="Error(s)" Binding="{Binding Path=Errors}" />
  </DataGrid.Columns>
</DataGrid>

Open in new window

Private _list As ArrayList = Nothing
...
 _list = FeatureDefinition.LoadList(...)
...
Dim qry = (From def As FeatureDefinition In _list ...)
...
SearchResultsDataGrid.ItemsSource = qry

Open in new window

0
 
Bob LearnedCommented:
Do you have a  full stack  trace for that exception that you are getting?
0
 
Craig YellickDatabase ArchitectAuthor Commented:
Thanks for the box/unbox links, that's another good reason to use List-of. I have converted all ArrayLists.

Digging into the inner exceptions (there are two nested), the lowest says "At least one object must implement IComparable." I think that's the root of the problem. The conversion class needs to implement IComparable. I thought it would do a string comparison by default but apparently not. The Convert function is typed as an Object so I guess it makes sense that the sort can't make assumptions about type.
Stack Trace: at System.Windows.Controls.DataGrid.DefaultSort(DataGridColumn column, Boolean clearExistingSortDescriptions)
   at System.Windows.Controls.DataGrid.OnSorting(DataGridSortingEventArgs eventArgs)
   at System.Windows.Controls.DataGrid.PerformSort(DataGridColumn sortColumn)
   at System.Windows.Controls.Primitives.DataGridColumnHeader.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.Controls.Primitives.DataGridColumnHeader.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 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(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.Threading.Dispatcher.Run()
   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 SPRooterToot.Application.Main() in C:\Projects\SPRooterToot\obj\x86\Debug\Application.g.vb:line 64
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Open in new window

0
 
Craig YellickDatabase ArchitectAuthor Commented:
I implemented IComparable in the converter class and it did not help (source code below). I tried excluding the SortMemberPath as well as explicitly setting it to the property name and got the same error.

There must be some kind of special syntax in SortMemberPath to indicate the use of the converted value.
Public Class TypeListToStringConverter
  Implements IValueConverter, IComparable
  Private result As String
  Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    Dim list As List(Of String) = CType(value, List(Of String))
    If list.Count = 0 Then
      result = "Uknown"
    ElseIf list.Count = 1 Then
      result = list(0).ToString
    Else
      result = "Multi (" & list.Count.ToString & ")"
    End If
    Return result
  End Function

  Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
    Throw New NotImplementedException()
  End Function

  Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
    Return Me.result.CompareTo(obj.ToString)
  End Function
End Class

Open in new window

0
 
Bob LearnedCommented:
Now this is another example of using a strong-type versus Object.

A GENERIC BASE CLASS FOR IMPLEMENTING IVALUECONVERTER
http://www.cookcomputing.com/blog/archives/000613.html

Public Class BooleanToVisibilityConverter
	Inherits BaseValueConverter(Of Boolean, Visibility)
	Protected Overrides Function Convert(value As Boolean, culture As CultureInfo) As Visibility
		Return If(value, Visibility.Visible, Visibility.Collapsed)
	End Function
End Class

Open in new window


Public Class BooleanToVisibilityConverter
	Inherits BaseValueConverter(Of Boolean, Visibility, [String])
	Protected Overrides Function Convert(value As Boolean, parameter As String, culture As CultureInfo) As Visibility
		If parameter IsNot Nothing AndAlso parameter = "!" Then
			value = Not value
		End If
		Return If(value, Visibility.Visible, Visibility.Collapsed)
	End Function
End Class

Open in new window

0
 
Craig YellickDatabase ArchitectAuthor Commented:
Great idea about strongly-typed converters. I might give that a try. In the interest of getting past this problem I added a read-only property to that class which performs the same summarizing function. Now I can just bind the column to the property and get on with it.  I'll probably return to this eventually but for now it'll have to do.

THANKS for all your help.
0
 
Craig YellickDatabase ArchitectAuthor Commented:
While this thread did not not (at this time) solve the sorting problem, it is full of useful comments about using strongly-typed collections and value converters.
0
 
Bob LearnedCommented:
That is another perfectly viable idea!!  Sometimes I go for the complicated first, instead of the easy...
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.