DocumentWindow CloseButtons dissapear on layout restore

Docking/MDI for WPF Forum

Posted 9 years ago by sean duggan - bank of america
Version: 10.2.0531
Avatar
When saving the DockSiteLayout using the serialize, after restoring the layout, the DocumentWindows no longer have the Close Button on them. I tried changing the value of IsCloseButtonOnTab to true/false on the TabbedMdiHost which now has no effect.

I have the following simple code example to clearly show the problem.

run the sample and click on "Save layout", then "Restore Layout" - the close buttons on the tabs are now gone.

Many Thanks,
Sean.


XAML Code :-

<Window x:Class="ActiProTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindowTest" Height="350" Width="525" xmlns:docking="http://schemas.actiprosoftware.com/winfx/xaml/docking">
<Grid>
<StackPanel>
<TextBox>test</TextBox>
<Button Name="Button1" Click="Button1Click">Change Close Buttons</Button>
<Button Name="Button2" Click="Button2Click">Save Layout</Button>
<Button Name="Button3" Click="Button3Click">Restore Layout</Button>
<docking:DockSite Name="dockSite" HorizontalAlignment="Left" Margin="166,100,0,0" VerticalAlignment="Top" Background="Gray">
<docking:Workspace>
<Grid>
<docking:TabbedMdiHost Name="tabbedMdiHost" IsCloseButtonOnTab="true" IsImageOnTab="True" CanDocumentsCloseOnMiddleClick="True">
<docking:TabbedMdiContainer>
</docking:TabbedMdiContainer>
</docking:TabbedMdiHost>
</Grid>
</docking:Workspace>
</docking:DockSite>
</StackPanel>
</Grid>
</Window>


CodeBehind :-

using System.Windows;
using ActiproSoftware.Windows.Controls.Docking;
using ActiproSoftware.Windows.Controls.Docking.Serialization;

namespace ActiProTest
{
public partial class MainWindow
{
private const string DockSiteLayoutXml = @"c:\temp\docsitelayout.xml";
private const string TestWindow = "testWindow";

public MainWindow()
{
InitializeComponent();
var newDocumentWindow = AddNewDocumentWindow();
dockSite.DocumentWindows.Add(newDocumentWindow);
newDocumentWindow.Activate();
}

private void Button1Click(object sender, RoutedEventArgs e)
{
tabbedMdiHost.IsCloseButtonOnTab = !tabbedMdiHost.IsCloseButtonOnTab;
}

private void Button2Click(object sender, RoutedEventArgs e)
{
var dockSiteLayoutSerializer = new DockSiteLayoutSerializer { SerializationBehavior = DockSiteSerializationBehavior.All };

dockSiteLayoutSerializer.SaveToFile(DockSiteLayoutXml, dockSite);
}

private void Button3Click(object sender, RoutedEventArgs e)
{
for (var index = dockSite.DocumentWindows.Count - 1 ; index >= 0; index--)
{
var documentWindow = dockSite.DocumentWindows[index];
documentWindow.Close();
documentWindow.Destroy();
}

var dockSiteLayoutSerializer = new DockSiteLayoutSerializer
{
DocumentWindowDeserializationBehavior =
DockingWindowDeserializationBehavior.AutoCreate
};

dockSiteLayoutSerializer.DockingWindowDeserializing += OnLayoutSerializerDockingWindowDeserializing;
dockSiteLayoutSerializer.LoadFromFile(DockSiteLayoutXml, dockSite);
}

private static void OnLayoutSerializerDockingWindowDeserializing(object sender, DockingWindowDeserializingEventArgs e)
{
if (e.Node.Name == TestWindow)
{
e.Window = AddNewDocumentWindow();
}
}

private static DocumentWindow AddNewDocumentWindow()
{
var documentWindow = new DocumentWindow { Title = TestWindow, Name = TestWindow };

return documentWindow;

}
}
}

Comments (5)

Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Sean,

When you restore the layout, the previous TabbedMdiHost is no longer used, as it's replaced by a new instance created during the deserialization process. Currently, properties like TabbedMdiHost.IsCloseButtonOnTab are not saved/restored.

You can use code like the following in your "Change Close Buttons" handler to properly toggle the IsCloseButtonOnTab (as tabbedMdiHost references the old/unused TabbedMdiHost):
TabbedMdiHost host = VisualTreeHelperExtended.GetFirstDescendant(this, typeof(TabbedMdiHost)) as TabbedMdiHost;
if (host != null)
    host.IsCloseButtonOnTab = !host.IsCloseButtonOnTab;
In the current version, you'd need to subscribe to the ObjectDeserialized event and "initialize" the TabbedMdiHost there. Something like:
var dockSiteLayoutSerializer = new DockSiteLayoutSerializer();
dockSiteLayoutSerializer.ObjectDeserialized += dockSiteLayoutSerializer_ObjectDeserialized;

...

void dockSiteLayoutSerializer_ObjectDeserialized(object sender, ItemSerializationEventArgs e) {
    TabbedMdiHost host = e.Item as TabbedMdiHost;
    if (host != null)
        host.IsCloseButtonOnTab = true;
}
We are discussing options for saving/restoring properties such as these, to make it a bit easier.


Actipro Software Support

Posted 9 years ago by sean duggan - bank of america
Avatar
ah, that makes sense. works a treat.

Many thanks.
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Sean,

We've updated the deserialization process so it will try to reuse the existing Workspace and TabbedMdiHost/StandardMdiHost, when possible. In your case, the TabbedMdiHost would retain it's settings and you wouldn't need to add special initialization code. And any x:Name backing fields will continue to be valid.

If MDI host can be switched between TabbedMdiHost and StandardMdiHost (as seen in our Features Demo), then you may still need initialization code. If a StandardMdiHost is currently loaded and a layout that contains a TabbedMdiHost is restored, then the TabbedMdiHost would still need to be initialized (and the StandardMdiHost would be discarded).

This new feature will be included in the next maintenance release.


Actipro Software Support

Posted 9 years ago by sean duggan - bank of america
Avatar
That sounds much better.

I was a bit concerned that the existing objects were still hanging around, and what would happen after several deserializations - i.e. would I then get lots of redundant objects left over after each Deserialization, which is how our app would work - save/load workspace will happen many times throughout the day.

How do I get the update please?
Posted 9 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Sean,

In general, the previous Workspace/TabbedMdiHost/StandardMdiHost would be collected, except if you gave it an x:Name. By naming the element, it would keep a reference to the object in a backing field. So the original Workspace/TabbedMdiHost/StandardMdiHost would be retained, but one workaround would be to not name those elements (so no backing field was created).

In fact, the same holds true for TabbedMdiContainer, ToolWindowContainer, or SplitContainer. These elements should not be named as they are created/destroyed as needed. If they are named, then even if they are not used, then the backing field will retain them.

We are hoping to get the maintenance release out in early November.


Actipro Software Support

The latest build of this product (v2019.1 build 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.