RadPane.IsHidden + binding causes InvalidOperationException

4 Answers 180 Views
Docking
David
Top achievements
Rank 1
Iron
Iron
David asked on 24 May 2021, 04:04 PM

We have a RadPane styled with IsHidden bound to a viewmodel property.

 <Style x:Key="PaneStyle" TargetType="{x:Type telerik:RadPane}">
   <Setter Property="CanUserClose" Value="False" />
   <Setter Property="CanFloat" Value="False" />
   <Setter Property="CanUserPin" Value="False" />
   <Setter Property="ContextMenuTemplate" Value="{x:Null}" />
   <Setter Property="IsPinned" Value="True" />
</Style>
<Style x:Key="PaneXStyle" TargetType="{x:Type telerik:RadPane}" BasedOn="{StaticResource PaneStyle}">
     <Setter Property="IsHidden" Value="{Binding HidePaneX}" />
</Style>
 

with the styles used like so:

<telerik:RadDocking AllowUnsafeMode="True">
   <telerik:RadDocking.DocumentHost>
     <telerik:RadSplitContainer>
       <telerik:RadPaneGroup>
        <telerik:RadPane Style="{StaticResource PaneStyle}" Header="Some Panes">
          <telerik:RadDocking AllowUnsafeMode="True">
            <telerik:RadDocking.DocumentHost>
              <telerik:RadSplitContainer>
                <telerik:RadPaneGroup>
                  <telerik:RadPane Style="{StaticResource PaneXStyle}" Header="PaneX">
       ......
 

After updating recently to v. 2021.1.325, we get the following exception.  We'd previously been on a very old version (2015 q1 sp1).

System.InvalidOperationException: Cannot modify the logical children for this node at this time because a tree walk is in progress.
   at System.Windows.FrameworkElement.RemoveLogicalChild(Object child)
   at MS.Internal.Controls.InnerItemCollectionView.ClearModelParent(Object item)
   at MS.Internal.Controls.InnerItemCollectionView._RemoveAt(Int32 index, Int32 indexR, Object item)
   at System.Windows.Controls.ItemCollection.Remove(Object removeItem)
   at Telerik.Windows.Controls.RadPaneGroup.ClosePane(RadPane pane) in c:\DeveloperTooling_Agent13\_work\103\s\Controls\Docking\Docking\Docking\RadPaneGroup.cs:line 671
   at Telerik.Windows.Controls.RadDocking.HidePane(RadPane pane) in c:\DeveloperTooling_Agent13\_work\103\s\Controls\Docking\Docking\Docking\RadDocking.cs:line 463
   at Telerik.Windows.Controls.RadPane.Close() in c:\DeveloperTooling_Agent13\_work\103\s\Controls\Docking\Docking\Docking\RadPane.cs:line 1566
   at Telerik.Windows.Controls.RadPane.OnIsHiddenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) in c:\DeveloperTooling_Agent13\_work\103\s\Controls\Docking\Docking\Docking\RadPane.cs:line 1319
   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.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue)
   at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
   at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
   at System.Windows.Data.BindingExpression.Activate(Object item)
   at System.Windows.Data.BindingExpression.OnDataContextChanged(DependencyObject contextElement)
   at System.Windows.Data.BindingExpression.HandlePropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
   at System.Windows.Data.BindingExpressionBase.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
   at System.Windows.Data.BindingExpression.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
   at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
   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.TreeWalkHelper.InvalidateTreeDependentProperty(TreeChangeInfo info, DependencyObject d, FrameworkObject& fo, DependencyProperty dp, FrameworkPropertyMetadata fMetadata, Style selfStyle, Style selfThemeStyle, ChildRecord& childRecord, Boolean isChildRecordValid, Boolean hasStyleChanged, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
   at System.Windows.TreeWalkHelper.InvalidateTreeDependentProperties(TreeChangeInfo info, FrameworkElement fe, FrameworkContentElement fce, Style selfStyle, Style selfThemeStyle, ChildRecord& childRecord, Boolean isChildRecordValid, Boolean hasStyleChanged, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
   at System.Windows.FrameworkElement.InvalidateTreeDependentProperties(TreeChangeInfo parentTreeState, Boolean isSelfInheritanceParent, Boolean wasSelfInheritanceParent)
   at System.Windows.FrameworkElement.OnAncestorChangedInternal(TreeChangeInfo parentTreeState)
   at System.Windows.TreeWalkHelper.OnAncestorChanged(DependencyObject d, TreeChangeInfo info, Boolean visitedViaVisualTree)
   at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
   at MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
   at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren)
   at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren)
   at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d)
   at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
   at MS.Internal.PrePostDescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree)
   at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren)
   at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren)
   at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d)
   at System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
   at MS.Internal.PrePostDescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode)
   at System.Windows.TreeWalkHelper.InvalidateOnTreeChange(FrameworkElement fe, FrameworkContentElement fce, DependencyObject parent, Boolean isAddOperation)
   at System.Windows.FrameworkElement.ChangeLogicalParent(DependencyObject newParent)
   at System.Windows.FrameworkElement.AddLogicalChild(Object child)
   at Telerik.Windows.Controls.RadDocking.OnLoaded(Object sender, RoutedEventArgs e) in c:\DeveloperTooling_Agent13\_work\103\s\Controls\Docking\Docking\Docking\RadDocking.cs:line 1756
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root, RoutedEvent routedEvent)
   at System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(Object root)
   at MS.Internal.LoadedOrUnloadedOperation.DoWork()
   at System.Windows.Media.MediaContext.FireLoadedPendingCallbacks()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

 

For the time being, we instead bind IsEnabled to our VM property, but we'd like to be able to hide these panes again, please.

Additionally, the exception doesn't happen if the pane happens to be visible when the application starts; rather, it only happens if the pane is out of view (one of the other panes in the top-level Docking is foremost) and we browse to it later.  It is still docked during this time.

 

Is this a bug for which we should file a report?  Is there a different or better way to do this?

 

David
Top achievements
Rank 1
Iron
Iron
commented on 24 May 2021, 04:04 PM

additionally, I do see that someone had a similar problem
https://www.telerik.com/forums/style-panesource-and-save-load-layout
but the solution of moving the styles around isn't applicable/possible here.

4 Answers, 1 is accepted

Sort by
0
Dinko | Tech Support Engineer
Telerik team
answered on 27 May 2021, 06:19 AM

Hello David,

Thank you for the provided details.

Changing the value of the IsHidden property could cause the observed exception. If the property is changed through binding, the DataContext could be lost due to a logic implemented in the application (for example when moving instances between different visual trees) and that to cause the observed by you issue.  In order to resolve such issues all you need to do is to make sure that the DataContext of such RadPane instance is explicitly set to the same DataContext which otherwise would be inherited (the RadDocking DataContext). The next code snippet shows the described approach:

<telerik:RadDocking AllowUnsafeMode="True" x:Name="radDocking">
   <telerik:RadDocking.DocumentHost>
     <telerik:RadSplitContainer>
       <telerik:RadPaneGroup>
        <telerik:RadPane Style="{StaticResource PaneStyle}" Header="Some Panes">
          <telerik:RadDocking AllowUnsafeMode="True">
            <telerik:RadDocking.DocumentHost>
              <telerik:RadSplitContainer>
                <telerik:RadPaneGroup>
                  <telerik:RadPane Style="{StaticResource PaneXStyle}" Header="PaneX" DataContext="{Binding ElementName=radDocking, Path=DataContext}">

Can you try this approach and let me know if it works for you.

Regards,
Dinko
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

David
Top achievements
Rank 1
Iron
Iron
commented on 27 May 2021, 04:22 PM

Thank you very much for the reply! I will try this and get back to you!
David
Top achievements
Rank 1
Iron
Iron
commented on 27 May 2021, 09:45 PM

This turns out not to have worked. When I try this, there is a binding error, "Cannot find source: ElementName=radDocking". I double-checked that spelling etc were correct.
David
Top achievements
Rank 1
Iron
Iron
commented on 27 May 2021, 10:00 PM

I also could not set the binding source using a RelativeSource with AncestorType of RadDocking & AncestorLevel of 2.
0
Vladimir Stoyanov
Telerik team
answered on 01 Jun 2021, 09:22 AM

Hello David,

Thank you for the shared information. 

My current understanding is that the exception is received when setting the IsHidden property of a RadPane in the inner RadDocking control. Additionally, the RadPane, which hosts the inner RadDocking should not be the selected pane initially. Feel free to correct me, if I am missing something and elaborate on the scenario. 

I tried to replicate the scenario on my end in order to investigate it, however I was not able to do so. That is why I am attaching the sample project that I used for testing purposes. May I ask you to check it out and see how it differs from the setup on your end? Can you update the project in order to demonstrate the scenario and send it back? This will hopefully allow me to investigate further and better assist you. 

Thank you in advance for any help you can provide.

Regards,
Vladimir Stoyanov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

David
Top achievements
Rank 1
Iron
Iron
commented on 02 Jun 2021, 02:01 PM

Thanks, Vladimir! I will give this a try and see whether I can get it to fail.
0
David
Top achievements
Rank 1
Iron
Iron
answered on 02 Jun 2021, 09:28 PM

Here is an example that fails in the way I usually see it fail, and it very closely mimics our actual application.

- as written, I get an exception when first navigating to Pane X

- if I replace the DataContext (see where it's commented out), there is the binding error I mentioned before (where PaneX never is hidden).

 

The notable changes are a) that there is a UserControl with most of the content, and b) the styles are defined in that UserControl.

As I mentioned in the initial question, I did find a thread from some time ago in which the solution was to put all the styles in App.xaml, but that's not really viable in this case (it's a large application, and, for locality of reference, we would prefer to have the styles for the SomePanes control defined at or near there).

 

0
Dinko | Tech Support Engineer
Telerik team
answered on 03 Jun 2021, 11:34 AM

Hello David,

Thank you for the provided project.

I was able to observe this behavior. This behavior results from the propagation process of the DataContext to the nested panes when DocumentHost is used. To make it work, you can first set the DataContext of the RadPane in XAML to the RadDocking one, then in the Loaded event of the RadPane, you can again set it to the UserControl.

. . . . .
  <telerik:RadPane Style="{StaticResource PaneXStyle}" Header="PaneX" Loaded="RadPane_Loaded" DataContext="{Binding DataContext, ElementName=docking}"/>
. . . . .

private void RadPane_Loaded(object sender, RoutedEventArgs e)
{
	var pane = sender as RadPane;
	pane.DataContext = this.DataContext;
}

Another approach will be to avoid using DocumentHost functionality. I have tested this and it is working as expected.

Regards,
Dinko
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Tags
Docking
Asked by
David
Top achievements
Rank 1
Iron
Iron
Answers by
Dinko | Tech Support Engineer
Telerik team
Vladimir Stoyanov
Telerik team
David
Top achievements
Rank 1
Iron
Iron
Share this question
or