I have a XamDockManager control with MDI-style ContentPanes. My tabs are bound. I am using Attached Properties to manage the tabs. I am using using Dependency Properties to handle loading and saving the layout.
On start up everything works as expected. My problem starts when I tear off one of my previously tabbed panes. SaveLayout() work correctly. If I shut down the app and restart it the load fails during xamDockManager.LoadLayout(path_to_saved_layout).
"Specified element is already the logical child of another element. Disconnect it first."
If I leave all of my bound, MDI-style panes as-is on start-up, there are no problems reloading the layout original layout. Tearing off non-bound panes, doesn't cause any problems either.
Any suggestions on how to get around this problem while still allowing my users the ability to save their custom layouts?
This seems to be a bug in your code. You're repopulating the tabgroup's Items collection with all the items while the layout is being loaded even though some of those items were reparented elsewhere as part of the loadlayout. I think if you set your layoutApplied flag to true before calling LoadLayout (in your LoadPaneLayout method) this could be avoided. Also you have a bug in that same TabGroupContentChangedHandler where you are still adding items even if the layoutApplied is true; since you don't have the indented code in a block only the clear call is conditional to the !layoutApplied check. So instead of:
if (!layoutApplied) tabGroupPane.Items.Clear(); foreach (var module in modules) if (module.ContentPane.Parent == null) tabGroupPane.Items.Add(module.ContentPane); [./code] It should be: [code] if (!layoutApplied) { tabGroupPane.Items.Clear(); foreach (var module in modules) if (module.ContentPane.Parent == null) tabGroupPane.Items.Add(module.ContentPane); }
if (!layoutApplied) tabGroupPane.Items.Clear(); foreach (var module in modules) if (module.ContentPane.Parent == null) tabGroupPane.Items.Add(module.ContentPane);
[./code]
It should be:
[code] if (!layoutApplied) { tabGroupPane.Items.Clear(); foreach (var module in modules) if (module.ContentPane.Parent == null) tabGroupPane.Items.Add(module.ContentPane); }
Here's the callstack I'm seeing that is causing the issue:
PresentationFramework.dll!System.Windows.Controls.ItemCollection.Clear() Line 268 + 0xb bytes C# DockManager.Ui.dll!DockManager.Ui.Behaviors.XamDockManagerBehaviors.TabGroupContentChangedHandler(System.Windows.DependencyObject obj, System.Windows.DependencyPropertyChangedEventArgs e) Line 239 + 0x18 bytes C# WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) Line 2035 + 0x2c bytes C# PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) Line 2067 + 0x22 bytes C# WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args) Line 1739 + 0x22 bytes C# WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) Line 1553 + 0xb4 bytes C# WindowsBase.dll!System.Windows.DependencyObject.InvalidateProperty(System.Windows.DependencyProperty dp) Line 1206 + 0x74 bytes C# PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Invalidate(bool isASubPropertyChange) Line 1226 + 0x12 bytes C# PresentationFramework.dll!System.Windows.Data.BindingExpression.TransferValue(object newValue, bool isASubPropertyChange) Line 1506 C# PresentationFramework.dll!System.Windows.Data.BindingExpression.Activate(object item) Line 1044 C# PresentationFramework.dll!System.Windows.Data.BindingExpression.OnDataContextChanged(System.Windows.DependencyObject contextElement) Line 2312 + 0x9 bytes C# PresentationFramework.dll!System.Windows.Data.BindingExpression.HandlePropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args) Line 2490 C# PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args) Line 316 + 0x23 bytes C# PresentationFramework.dll!System.Windows.Data.BindingExpression.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args) Line 280 + 0x21 bytes C# WindowsBase.dll!System.Windows.DependentList.InvalidateDependents(System.Windows.DependencyObject source, System.Windows.DependencyPropertyChangedEventArgs sourceArgs) Line 58 + 0x24 bytes C# WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args) Line 1764 C# WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) Line 1553 + 0xb4 bytes C# PresentationFramework.dll!System.Windows.TreeWalkHelper.InvalidateTreeDependentProperty(System.Windows.TreeChangeInfo info, System.Windows.DependencyObject d, ref MS.Internal.FrameworkObject fo, System.Windows.DependencyProperty dp, System.Windows.FrameworkPropertyMetadata fMetadata, System.Windows.Style selfStyle, System.Windows.Style selfThemeStyle, ref System.Windows.ChildRecord childRecord, bool isChildRecordValid, bool hasStyleChanged, bool isSelfInheritanceParent) Line 356 + 0x1a9 bytes C# PresentationFramework.dll!System.Windows.TreeWalkHelper.InvalidateTreeDependentProperties(System.Windows.TreeChangeInfo info, System.Windows.FrameworkElement fe, System.Windows.FrameworkContentElement fce, System.Windows.Style selfStyle, System.Windows.Style selfThemeStyle, ref System.Windows.ChildRecord childRecord, bool isChildRecordValid, bool hasStyleChanged, bool isSelfInheritanceParent) Line 286 C# PresentationFramework.dll!System.Windows.FrameworkElement.InvalidateTreeDependentProperties(System.Windows.TreeChangeInfo parentTreeState, bool isSelfInheritanceParent) Line 530 + 0x39 bytes C# PresentationFramework.dll!System.Windows.FrameworkElement.OnAncestorChangedInternal(System.Windows.TreeChangeInfo parentTreeState) Line 415 + 0x1e bytes C# PresentationFramework.dll!System.Windows.TreeWalkHelper.OnAncestorChanged(System.Windows.DependencyObject d, System.Windows.TreeChangeInfo info) Line 169 + 0x3c bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>._VisitNode(System.Windows.DependencyObject d) Line 422 C# PresentationFramework.dll!MS.Internal.PrePostDescendentsWalker<System.Windows.TreeChangeInfo>._VisitNode(System.Windows.DependencyObject d) Line 108 + 0xc bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.VisitNode(System.Windows.FrameworkElement fe) Line 380 + 0xc bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.VisitNode(System.Windows.DependencyObject d) Line 397 + 0x16 bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.WalkLogicalChildren(System.Windows.FrameworkElement feParent, System.Windows.FrameworkContentElement fceParent, System.Collections.IEnumerator logicalChildren) Line 201 + 0x2e bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.WalkFrameworkElementLogicalThenVisualChildren(System.Windows.FrameworkElement feParent, bool hasLogicalChildren) Line 307 C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.IterateChildren(System.Windows.DependencyObject d) Line 92 + 0x9 bytes C# PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.TreeChangeInfo>.StartWalk(System.Windows.DependencyObject startNode, bool skipStartNode) Line 66 + 0xb bytes C# PresentationFramework.dll!MS.Internal.PrePostDescendentsWalker<System.Windows.TreeChangeInfo>.StartWalk(System.Windows.DependencyObject startNode, bool skipStartNode) Line 69 + 0x11 bytes C# PresentationFramework.dll!System.Windows.TreeWalkHelper.InvalidateOnTreeChange(System.Windows.FrameworkElement fe, System.Windows.FrameworkContentElement fce, System.Windows.DependencyObject parent, bool isAddOperation) Line 135 + 0x12 bytes C# PresentationFramework.dll!System.Windows.FrameworkElement.ChangeLogicalParent(System.Windows.DependencyObject newParent) Line 336 C# PresentationFramework.dll!System.Windows.FrameworkElement.AddLogicalChild(object child) Line 205 + 0x13 bytes C# InfragisticsWPF4.DockManager.v10.3.dll!Infragistics.Windows.DockManager.DocumentContentHost.OnPanesCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) Line 344 + 0xb bytes C# System.dll!System.Collections.ObjectModel.ObservableCollection<Infragistics.Windows.DockManager.SplitPane>.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) Line 281 + 0x11 bytes C# InfragisticsWPF4.v10.3.dll!Infragistics.Collections.ObservableCollectionExtended<Infragistics.Windows.DockManager.SplitPane>.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) Line 734 + 0xb bytes C# InfragisticsWPF4.DockManager.v10.3.dll!Infragistics.Windows.DockManager.QueuedObservableCollection<Infragistics.Windows.DockManager.SplitPane>.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) Line 56 + 0xb bytes C# InfragisticsWPF4.v10.3.dll!Infragistics.Collections.ObservableCollectionExtended<Infragistics.Windows.DockManager.SplitPane>.InsertItem(int index, Infragistics.Windows.DockManager.SplitPane item) Line 688 + 0x2b bytes C# mscorlib.dll!System.Collections.ObjectModel.Collection<Infragistics.Windows.DockManager.SplitPane>.Add(Infragistics.Windows.DockManager.SplitPane item) + 0x6a bytes InfragisticsWPF4.DockManager.v10.3.dll!Infragistics.Windows.DockManager.LayoutManager.LoadLayout(Infragistics.Windows.DockManager.XamDockManager dockManager, System.IO.Stream stream) Line 483 + 0x13 bytes C# InfragisticsWPF4.DockManager.v10.3.dll!Infragistics.Windows.DockManager.XamDockManager.LoadLayout(System.IO.Stream stream) Line 2038 + 0xc bytes C# DockManager.Ui.dll!DockManager.Ui.Behaviors.XamDockManagerBehaviors.LoadPaneLayout(object sender) Line 153 + 0xe bytes C# DockManager.Ui.dll!DockManager.Ui.Behaviors.XamDockManagerBehaviors.XamDockManagerLoaded(object sender, System.Windows.RoutedEventArgs e) Line 102 + 0x8 bytes C#
HI,
I am just following up on this thread.
Please let me know if you need further assistance.
Sincerely, Matt Developer Support Engineer
Hi Matt,
Sorry, I got pulled off on another issue for a few days.
I saw the missing curly braces shortly after I posted the sample.
Right now, I've got everything "working" in my sample. Recent challenges have been related to handling tabs that are grouped outside the original context. When you close the group, only the top pane visibility is updated. The others remain visibly when they aren't. Our solution adds a event handler for ToolWindowLoaded then adding a nested handler for the e.WindowClosed event.. Inside the nested handler I'm updating each toolPaneWindow.Panes.Pane.Visibility = Collapsed.
One issue that has been a nagging problem is: what's the best way/time/place to save a layout when shutting down the app? Via the attached property, I've wired up a handler for Unloaded. It doesn't get called when I close my app. Right now, I've got Save() calls inside .LayoutUpdated & .RequestBringIntoView. I am not thrilled about having identical code in two places. My tests show that LayoutUpdated catches things like theme updates that RequestBringIntoView would miss. But LayoutUpdated doesn't catch things related to the Active pane management. As far as I can tell Unloaded gets called as the tabs are created, but it doesn't fire when you close the app.
Any suggestions?
Thx,
Ivan
Why don't you wire up the WindowClosing event and save your layout in that event
I started with that idea, but moved away from it for modularity. My goal is to have the XamDockManagement system living in a separate assembly from the App. I'll give it a shot today and see if it still works with the way everything is organized.
I am just following up on this tread.
Please let me know if you need further assistance regarding this issue.
Think I have it fixed.
I am following up on this thread.
Sincerely, Matt DSE
I am just following up on this thread. Please let me know if you need further assistance.
Sincerely,
Matt DSE
I am just following up on this forum thread.
Please let me know if you need further assistance. Sincerely. Matt Developer Support Engineer