Hi.
I have the following RadPropertyGrid:
XAML:
<Grid
behaviors:CalloutTagBehavior.CalloutTag="F6C90D8E-9F1C-4372-87D0-17B40EE1E462"
IsEnabled="{Binding StateDispatcher.EnvironmentState,
Converter={converters:ComparisonConverter},
ConverterParameter={x:Static runtime:EnvironmentState.Design}}">
<telerik:RadPropertyGrid
x:Name="RadPropertyGrid"
telerik:PropertySet.ShouldAddNullForNonMatchingValues="True"
AutoGeneratingPropertyDefinition="RadPropertyGrid_OnAutoGeneratingPropertyDefinition"
EditEnded="RadPropertyGrid_OnEditEnded"
EditMode="Single"
FieldIndicatorVisibility="Collapsed"
IsGrouped="False"
Item="{Binding SelectedObject}"
LabelColumnWidth="0.8*"
Loaded="RadPropertyGrid_OnLoaded"
PropertySetMode="None"
RenderMode="Flat">
<telerik:RadPropertyGrid.GroupStyle>
<Style TargetType="telerik:RadToggleButton">
<Setter Property="Margin" Value="0,0,15,0" />
</Style>
</telerik:RadPropertyGrid.GroupStyle>
</telerik:RadPropertyGrid>
</Grid>
CodeBehinde:
public partial class PropertyGridPaneUserControl : UserControl
{
public PropertyGridPaneUserControl(IEventAggregator eventAggregator)
{
if (eventAggregator == null) throw new ArgumentNullException(nameof(eventAggregator));
InitializeComponent();
eventAggregator.Register<RefreshPropertyGridMessage>(this, RefreshPropertyGridMessageHandler);
}
private void RefreshPropertyGridMessageHandler(RefreshPropertyGridMessage message)
{
RadPropertyGrid.ReloadData();
}
private void RadPropertyGrid_OnAutoGeneratingPropertyDefinition(object? sender, AutoGeneratingPropertyDefinitionEventArgs e)
{
((Binding)e.PropertyDefinition.Binding).ValidatesOnExceptions = true;
}
private void RadPropertyGrid_OnLoaded(object sender, RoutedEventArgs e)
{
var virtualizingStackPanel = RadPropertyGrid
.ChildrenOfType<VirtualizingStackPanel>().FirstOrDefault();
if (virtualizingStackPanel != null)
{
virtualizingStackPanel.ScrollOwner.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
}
private void RadPropertyGrid_OnEditEnded(object? sender, PropertyGridEditEndedEventArgs e)
{
if (e.EditAction != PropertyGridEditEndedAction.Commit ||
Equals(e.NewValue, e.OldValue) ||
DataContext is not PropertyGridViewModel {SelectedObjects: { }} propertyGridViewModel) return;
ItemPropertyInfo sourceProperty = e.EditedPropertyDefinition.SourceProperty;
foreach (var selectedObject in propertyGridViewModel.SelectedObjects)
{
if (selectedObject == e.EditedPropertyDefinition.Instance) continue;
PropertyInfo? property = selectedObject.GetType()
.GetProperty(sourceProperty.Name, sourceProperty.PropertyType);
if (property != null)
{
property.SetValue(selectedObject, e.NewValue);
}
}
}
}
And it worked fine up to a certain point. The problem occurs when processing an object with the ObservableCollection property of the following type:
public class A
{
public ObservableCollection<object> Collection { get; }
}
My task is to perform asynchronous addition of elements to a nested collection. If I didn't open the CollectionEditor in the UI, then the addition happens without problems. But if you open CollectionEditor in UI at least once, InvalidOperationnException is generated when adding asynchronously: The calling thread cannot access this object because a different thread owns it. Stack trace part:
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
at System.Windows.Controls.Panel.get_IsItemsHost()
at System.Windows.Controls.ItemsControl.GetItemsOwnerInternal(DependencyObject element, ItemsControl& itemsControl)
at System.Windows.Controls.Panel.VerifyBoundState()
at System.Windows.Controls.Panel.OnItemsChanged(Object sender, ItemsChangedEventArgs args)
at System.Windows.Controls.ItemContainerGenerator.OnItemAdded(Object item, Int32 index)
at System.Windows.Controls.ItemContainerGenerator.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at System.Windows.WeakEventManager.ListenerList`1.DeliverEvent(Object sender, EventArgs e, Type managerType)
at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args)
at System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Windows.WeakEventManager.ListenerList`1.DeliverEvent(Object sender, EventArgs e, Type managerType)
at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args)
at System.Windows.Data.CollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
at MS.Internal.Data.CollectionViewProxy._OnViewChanged(Object sender, NotifyCollectionChangedEventArgs args)
at Telerik.Windows.Data.QueryableCollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
at Telerik.Windows.Data.QueryableCollectionView.ProcessSynchronousCollectionChangedWithAdjustedArgs(NotifyCollectionChangedEventArgs originalArguments, Int32 adjustedOldIndex, Int32 adjustedNewIndex)
at Telerik.Windows.Data.QueryableCollectionView.ProcessSynchronousCollectionChanged(NotifyCollectionChangedEventArgs args)
at Telerik.Windows.Data.QueryableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at Telerik.Windows.Data.QueryableCollectionView.OnSourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at Telerik.Windows.Data.QueryableCollectionView.Telerik.Windows.Data.IWeakEventListener<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.ReceiveWeakEvent(Object sender, NotifyCollectionChangedEventArgs args)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
At the same time, an exception occurs even if in Rad PropertyGrid.Item set another instance of the object or null. I would like to know how to correct this misunderstanding
The WPF framework doesn't allow making modifications to the UI elements on another thread (different than the UI thread). This leads to InvalidOperationnException (" The calling thread cannot access this object because a different thread owns it.").
To avoid this type of error, you should move all operations related to the UI from the thread that executes it to the WPF UI thread. This includes updates of properties bound to the UI. The moving of the code is done using a Dispatcher. For example:
public void UpdateCollectionOnAnotherThread() { // some actions here App.Current.Dispatcher.BeginInvoke(new Action(() => { this.Collection.Add(newItem); })); // some actions here }