Hi there, sorry for the length of this post, this is a particularly tricky
problem to explain
I've been evaluating the Xceed grid and have been very impressed to date,
but I fear I may have uncovered a bug.
To set the scenario, I have an application that uses a control app to spawn
multiple windows, each with a dedicated UI thread. In each of these
windows I have a grid which displays large volumes of data, with column
definitions configurable by the user. When I start up the first window
all is well, the grid is bound and the data displays and updates as
appropriate. When I start up the second window I get a cross thread
exception when trying to set the ItemsSource of the DataGridControl to the View
property of my DataGridCollectionViewSource.
The grid is hosted in a UserControl, which is then hosted in the main
Window. The grid specific xaml is:
<xcdg:DataGridControl
x:Name="_mainGrid"
ReadOnly="True"
ItemScrollingBehavior="Immediate"
NavigationBehavior="RowOnly" >
<xcdg:DataGridControl.Resources>
<Style TargetType="{x:Type Views:TableflowView}">
<Setter Property="AllowColumnChooser" Value="False"
/>
</Style>
</xcdg:DataGridControl.Resources>
<xcdg:DataGridControl.View>
<Views:TableflowView Theme="{DynamicResource defaultTheme}"
RowFadeInAnimationDuration="100" ScrollingAnimationDuration="300"
UseDefaultHeadersFooters="False" ShowScrollTip="False">
<Views:TableflowView.FixedHeaders>
<DataTemplate>
<xcdg:HierarchicalGroupByControl Views:TableView.CanScrollHorizontally="False"
/>
</DataTemplate>
<DataTemplate>
<xcdg:ColumnManagerRow />
</DataTemplate>
</Views:TableflowView.FixedHeaders>
</Views:TableflowView>
</xcdg:DataGridControl.View>
</xcdg:DataGridControl>
The important part of the data binding code (with the thread logging in it) is below:
public void
SetDataBinding<T>(ObservableCollection<T>
data, IEnumerable<DynamicColumnDefinition> columnDefs)
{
var viewSource = new DataGridCollectionViewSource();
_mainGrid.Columns.Clear();
// set up the columns
foreach (var
columnDef in columnDefs)
{
var columnItem = new DataGridItemProperty(columnDef.PropertyName,
columnDef.PropertyName, typeof (object));
viewSource.ItemProperties.Add(columnItem);
// build the grid column
var dataColumn =
getColumn(columnDef.Type);
dataColumn.Title = columnDef.Header;
dataColumn.FieldName = columnDef.Header;
_mainGrid.Columns.Add(dataColumn);
}
// general threading info
Console.WriteLine("Current
Dispacher Thread = {0}", Dispatcher.Thread.ManagedThreadId);
Console.WriteLine("Current
Operational Thread = {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("CheckAccess
State = {0}", Dispatcher.CheckAccess());
// ViewSource threading info
Console.WriteLine("Current
Grid Thread = {0}", viewSource.Dispatcher.Thread.ManagedThreadId);
Console.WriteLine("Grid
CheckAccess State = {0}", viewSource.Dispatcher.CheckAccess());
// grid threading info
Console.WriteLine("Current
Grid Thread = {0}", _mainGrid.Dispatcher.Thread.ManagedThreadId);
Console.WriteLine("Grid
CheckAccess State = {0}", _mainGrid.Dispatcher.CheckAccess());
viewSource.Source = data;
_mainGrid.ItemsSource = viewSource.View; // cross-thread
exception here
}
On the run that crashes the output of the console output reads:
Current Dispacher Thread = 25
Current Operational Thread = 25
CheckAccess State = True
Current Grid Thread = 25
Grid CheckAccess State = True
Current Grid Thread = 25
Grid CheckAccess State = True
But I still get the dreaded cross thread exception as follows:
System.InvalidOperationException
The calling thread cannot access this object because a different thread owns
it.
Stack trace:
at System.Windows.DependencyObject.GetValue(DependencyProperty
dp)
at
System.Windows.FrameworkElement.get_IsLoaded()
at
System.Windows.FrameworkElement.FindResourceInternal(FrameworkElement fe,
FrameworkContentElement fce, DependencyProperty dp, Object resourceKey, Object
unlinkedParent, Boolean allowDeferredResourceReference, Boolean
mustReturnDeferredResourceReference, DependencyObject boundaryElement, Boolean
isImplicitStyleLookup, Object& source)
at
System.Windows.FrameworkElement.TryFindResource(Object resourceKey)
at
Xceed.Wpf.DataGrid.DefaultCellEditorSelector.SelectCellEditor(Type dataType)
at
Xceed.Wpf.DataGrid.ItemsSourceHelper.GenerateColumnsFromItemsSourceFields(ColumnCollection
columns, IDictionary`2 defaultCellEditors, Dictionary`2 fields, Boolean
autoCreateForeignKeyConfigurations)
at
Xceed.Wpf.DataGrid.DataGridControl.GenerateColumnsFromItemsSourceFields()
at
Xceed.Wpf.DataGrid.DataGridControl.ProcessDelayedItemsSourceChanged()
at
Xceed.Wpf.DataGrid.DataGridControl.OnItemsSourceChanged(IEnumerable oldValue,
IEnumerable newValue)
at
System.Windows.Controls.ItemsControl.OnItemsSourceChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
at
System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs
e)
at
System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs
e)
at
System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs
args)
at
System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex,
DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry,
EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean
coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty
dp, Object value, PropertyMetadata metadata, Boolean
coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType
operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty
dp, Object value)
at
Forge2.UI.Controls.DynamicGrid.DynamicGrid.SetDataBinding[T](ObservableCollection`1
data, IEnumerable`1 columnDefs) in
C:\Enterprise\DOTNET\Forge2\Forge2.UI\Controls\DynamicGrid\DynamicGrid.xaml.cs:line
115
Given all the thread Id and access checks I do I cannot
possibly see how the threads could get crossed in my code. Either I am setting up the data source
incorrectly, or, I fear there is something amiss with the binding management of
the DataGridControl.
Please help, this is a complete show-stopper for this
otherwise excellent product.
Kind regards
Simon