I am having some problems setting up some custom databindings for columns in a datagrid in my code behind file. I see the data that I expect after setting up the bindings, and managing the bindings in response occasional to
changes in the columns I want to display seems to work fine. However, I often see some odd behaviour in the output window that indicates that the binding are failing. This is in a situation where the columns have been removed from the datagrid and suggests
that I am not completely clearing the bindings.
I set up the binding to the column as follows:
DataGridTextColumn newColumn = new DataGridTextColumn();
newColumn.Binding = new Binding("MyPath");
Later, I remove the DataGridColumns from the grid. However, even though they are no longer in the columns collection (and presumably the visual tree), I see binding exceptions which suggest that the bindings are still hooked up.
As well as removing the columns from the column collection, I have attempted to set the binding(s) to null before the columns are removed, but this does not fix the issue.
Reading up on the subject a bit more suggests that I might need to clear the bindings using the following syntax:
DataGridBoundColumn dgColumn = this.mainTable.Columns[i] as DataGridBoundColumn;
However, I am not sure what parameters to pass to the ClearBinding method, in particular, what is the property name of the binding property on a DataGridBoundColumn?
Clearly I'm doing something wrong but don't know whether I'm on the right track to solving the issue and it's driving me mad!
As an aside, I'm doing the manual binding in order to flatten a list of objects so that it maps to a single row.
Apr 7, 2009 at 9:22 PM
Can you please send us the output window traces and also a repro project if possible.
Hi, will try to sort something out tomorrow.
Apr 8, 2009 at 3:13 PM
Edited Apr 8, 2009 at 3:20 PM
Ok, I am learning more about what is going on...
I have a datagrid whose ItemSource is bound to a field CurrentStats of type ObservableCollection<MyStats>. The MyStats type contains a field "TheStats" of type ObervableCollection<StatsGroup>. The DataGridColumns for the DataGrid have
their bindings created in code so that the columns are bound to different properties on StatsGroup, for all StatsGroups in the collection. The result is that I have a table like so (data in MyStats is flattened into a single row:)
Item 1 StatsGroup PropA | Item 1 StatsGroup PropB | Item 2 StatsGroup PropA | Item 2 StatsGroup PropB | Item 3 StatsGroup PropA | Item 3 StatsGroup PropB | etc
Min | | | | | |
Max | | | | | |
Average| | | | | | |
When I want to recalculate the statistics shown in the grid (in response to a change in the raw data), I clear the CurrentStats ObservableCollection and then update the MyStats data before adding it back to the collection. NB The datagrid appears to clear at
this point. However, I see a cascade of binding errors in the output window as the private MyStats fields are recalculated, but before they are added back into the collection. These errors are of the form:
System.Windows.Data Error: 16 : Cannot get 'Item' value (type 'StatsGroup') from 'TheStats' (type 'ObservableCollection`1'). BindingExpression:Path=TheStats.PropA; DataItem=MyStats' (HashCode=1605556); target element is 'TextBlock' (Name=''); target
property is 'Text' (type 'String') TargetInvocationException:'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and
less than the size of the collection.
Parameter name: index
at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.get_Item(Int32 index)
at System.Collections.ObjectModel.Collection`1.get_Item(Int32 index)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
Note, I have also attempted to clear the DataGrid ItemSource and then set it once the stats have been updated, but it makes no difference. It seems as though the grid is still bound to the underlying data as
it updates, even though I have cleared or reset the collection.
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object index, CultureInfo culture)
at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'
I can now hazard a guess as to what is going wrong. In my code, when I update the MyStats object(s) I call Clear on the TheStats ObservableCollection as so:
// About to update the stats, clear out the current info
Stepping over these calls in the debugger results in the binding exceptions being thrown. This is even if there is
no data in the grid (rows and columns have been removed before the update). To overcome this problem, I now create new objects (MyStatsMinimum = new ..., etc) rather than clearing the TheStats collections.
Perhaps this has something to do with the way ObservableCollections raise CollectionChanged events. When items are added or removed, the arguments to the event include a list of the items removed or added from the collection. However, if Clear is called, you
still get a CollectionChanged event, (this time with the Reset action), but no information is passed to the event handler about the items that were removed in response to the Clear call. I've always been surprised by this design decision, since it means you
may have to cache information about what was in the collection before it was cleared in order to properly clean up any hooks*. Perhaps there is a handler for the bindings that is falling into this trap somewhere?
* Some people have started to write versions of the ObservableCollection that prevent this behaviour or manually iterate over the items in response to the clear call. I think you can find an example in the SilverLight charting controls.