/// <inheritdoc/> public void CloseAllHiddenWindows() { foreach (var pane in AvalonDockHelper.GetAllAnchorables(dockingLayoutManager.DockingManager).Where(p => string.IsNullOrEmpty(p.ContentId) && p.IsHidden).ToList()) { CleanEditorPane(pane); RemoveEditorPane(pane); } }
/// <summary> /// Opens (and activates) an editor window for the given asset. If an editor window for this asset already exists, simply activates it. /// </summary> /// <param name="asset">The asset for which to show an editor window.</param> /// <param name="saveSettings">True if <see cref="MRUAdditionalData.OpenedAssets"/> should be updated, false otherwise. Note that if the editor fail to load it will not be updated.</param> /// <returns></returns> internal async Task OpenAssetEditorWindow([NotNull] AssetViewModel asset, bool saveSettings) { if (asset == null) { throw new ArgumentNullException(nameof(asset)); } if (dockingLayoutManager == null) { throw new InvalidOperationException("This method can only be invoked on the IEditorDialogService that has the editor main window as parent."); } // Switch to the editor layout before adding any Pane if (assetEditors.Count == 0) { dockingLayoutManager.SwitchToEditorLayout(); } using (await mutex.LockAsync()) { LayoutAnchorable editorPane = null; IEditorView view; // Asset already has an editor? if (asset.Editor != null) { // Look for the corresponding pane if (!assetEditors.TryGetValue(asset.Editor, out editorPane)) { // Inconsistency, clean leaking editor RemoveAssetEditor(asset); // Try to find if another editor currently has this asset var editor = assetEditors.Keys.OfType <IMultipleAssetEditorViewModel>().FirstOrDefault(x => x.OpenedAssets.Contains(asset)); if (editor != null) { editorPane = assetEditors[editor]; asset.Editor = editor; } } } // Existing editor? if (editorPane != null) { // Make the pane visible immediately MakeActiveVisible(editorPane); view = editorPane.Content as IEditorView; if (view?.EditorInitialization != null) { // Wait for the end of the initialization await view.EditorInitialization; } return; } // Create a new editor view view = asset.ServiceProvider.Get <IAssetsPluginService>().ConstructEditionView(asset); if (view != null) { // Pane may already exists (e.g. created from layout saving) editorPane = AvalonDockHelper.GetAllAnchorables(dockingLayoutManager.DockingManager).FirstOrDefault(p => p.Title == asset.Url); if (editorPane == null) { editorPane = new LayoutAnchorable { CanClose = true }; // Stack the asset in the dictionary of editor to prevent double-opening while double-clicking twice on the asset, since the initialization is async AvalonDockHelper.GetDocumentPane(dockingLayoutManager.DockingManager).Children.Add(editorPane); } editorPane.IsActiveChanged += EditorPaneIsActiveChanged; editorPane.IsSelectedChanged += EditorPaneIsSelectedChanged; editorPane.Closing += EditorPaneClosing; editorPane.Closed += EditorPaneClosed; editorPane.Content = view; // Make the pane visible immediately MakeActiveVisible(editorPane); // Initialize the editor view view.DataContext = asset; // Create a binding for the title var binding = new Binding(nameof(AssetViewModel.Url)) { Mode = BindingMode.OneWay, Source = asset }; BindingOperations.SetBinding(editorPane, LayoutContent.TitleProperty, binding); var viewModel = await view.InitializeEditor(asset); if (viewModel == null) { // Could not initialize editor CleanEditorPane(editorPane); RemoveEditorPane(editorPane); } else { assetEditors[viewModel] = editorPane; openedAssets.Add(asset); var multiEditor = viewModel as IMultipleAssetEditorViewModel; if (multiEditor != null) { foreach (var item in multiEditor.OpenedAssets) { if (item.Editor != null) { // Note: this could happen in some case after undo/redo that involves parenting of scenes RemoveAssetEditor(item); } item.Editor = viewModel; } multiEditor.OpenedAssets.CollectionChanged += (_, e) => MultiEditorOpenAssetsChanged(multiEditor, e); } else { asset.Editor = viewModel; } } } } // If the opening of the editor failed, go back to normal layout if (assetEditors.Count == 0) { dockingLayoutManager.SwitchToNormalLayout(); return; } if (saveSettings) { dockingLayoutManager.SaveOpenAssets(OpenedAssets); } }
private void ApplyDockingLayout(string text) { // Save the binding expressions of all the current anchorables var bindings = new Dictionary <string, List <BindingInfo> >(); var contentIdToBehaviors = new Dictionary <string, Behavior[]>(); foreach (var anchorable in AvalonDockHelper.GetAllAnchorables(DockingManager).Where(x => !string.IsNullOrEmpty(x.ContentId))) { var titleBindingInfo = BindingInfo.FromBindingExpression(BindingOperations.GetBindingExpression(anchorable, LayoutContent.TitleProperty)); var isVisibleBindingInfo = BindingInfo.FromBindingExpression(BindingOperations.GetBindingExpression(anchorable, AvalonDockHelper.IsVisibleProperty)); bindings.Add(anchorable.ContentId, new List <BindingInfo> { titleBindingInfo, isVisibleBindingInfo }); // Save behaviors, although complex bindings (eg. nested bindings) will not be restored properly... var behaviorCollection = Interaction.GetBehaviors(anchorable); if (behaviorCollection.Count > 0) { var pendingBehaviours = behaviorCollection.ToArray(); behaviorCollection.Clear(); contentIdToBehaviors.Add(anchorable.ContentId, pendingBehaviours); } } // Unregister docking manager AvalonDockHelper.UnregisterDockingManager(DockingManager); // This is a bit of a hack, but we need to do this with AssetPreview due to how bad the LayoutAnchorable is on determining // if it's actually visible or not. var assetPreviewAnchorable = AvalonDockHelper.GetAllAnchorables(DockingManager).FirstOrDefault(x => string.Equals(x.ContentId, "AssetPreview")); if (assetPreviewAnchorable != null) { gameStudioWindow.UnregisterAssetPreview(assetPreviewAnchorable); } // Deserialize the string using (var stream = new MemoryStream()) { var writer = new StreamWriter(stream); writer.Write(text); writer.Flush(); stream.Seek(0, SeekOrigin.Begin); var serializer = new XmlLayoutSerializer(DockingManager); serializer.Deserialize(stream); } // Apply saved the binding expressions to the newly deserialized anchorables foreach (var anchorable in AvalonDockHelper.GetAllAnchorables(DockingManager).Where(x => !string.IsNullOrEmpty(x.ContentId))) { List <BindingInfo> bindingInfos; if (bindings.TryGetValue(anchorable.ContentId, out bindingInfos)) { foreach (var bindingInfo in bindingInfos) { BindingOperations.SetBinding(anchorable, bindingInfo.Property, bindingInfo.Binding); } } if (contentIdToBehaviors.TryGetValue(anchorable.ContentId, out var pendingBehaviours)) { // Restore behaviors var behaviorCollection = Interaction.GetBehaviors(anchorable); foreach (var b in pendingBehaviours) { b.Attach(anchorable); behaviorCollection.Add(b); } } } // Re-register docking manager with new layout AvalonDockHelper.RegisterDockingManager(session.ServiceProvider, DockingManager); // Hack: need to get AssetPreview and register handlers again assetPreviewAnchorable = AvalonDockHelper.GetAllAnchorables(DockingManager).FirstOrDefault(x => string.Equals(x.ContentId, "AssetPreview")); if (assetPreviewAnchorable != null) { gameStudioWindow.RegisterAssetPreview(assetPreviewAnchorable); } }