internal void LoadWindowState(ToolsUIWindow mainWindow) { this.mainWindow = mainWindow; try { var defaultStateText = GetDefaultWindowStateResourceText(); var stateDoc = XElement.Parse(defaultStateText); var layoutDefinitionsElement = stateDoc.Element("LayoutDefinitions"); this.defaultLayoutDefinitions = layoutDefinitionsElement.Elements("LayoutDefinition") .Select(e => LayoutDefinition.LoadFromState(e, this.viewCreators)) .ToList(); } catch (Exception) { this.defaultLayoutDefinitions = new List <LayoutDefinition>(); } LoadWindowState(); }
void TryLoadWindowState() { if (!File.Exists(this.windowStateFileName)) { // Load nothing. return; } XElement windowStateRoot = XElement.Load(this.windowStateFileName); XAttribute versionAttr = windowStateRoot.Attribute("Version"); XElement mainWindowState = windowStateRoot.Element("MainWindow"); XElement layoutState = windowStateRoot.Element("LayoutDefinitions"); int version; if (versionAttr == null || !int.TryParse(versionAttr.Value, out version) || version != WindowStateVersion) { throw new VersionMismatchException(); } var bumpedDocumentFactories = new HashSet <string>(); var defaultLayoutVersions = windowStateRoot.Element("DefaultLayoutVersions"); if (defaultLayoutVersions != null) { foreach (var layoutVersion in defaultLayoutVersions.Elements("DefaultLayoutVersion")) { var factory = this.ExtensionManager.LookupDocumentFactory(layoutVersion.Attribute("DocumentFactoryName").Value); if (int.Parse(layoutVersion.Attribute("Version").Value) < factory.DefaultLayoutVersion) { bumpedDocumentFactories.Add(factory.Name); } } } // Don't call clear here, instead remove one at a time. Reason: Calling Clear() // sends a "Reset" event, and the layout handling code in ToolsUIWindow gets mad at that. // Usually doesn't matter (at startup), but this also gets called as part of "Revert // to Default Window State". while (this.LayoutDefinitions.Count > 0) { this.LayoutDefinitions.RemoveAt(0); } var usedIds = new HashSet <Guid>(); var loadedLayoutDefs = new Dictionary <string, List <LayoutDefinition> >(); foreach (var layoutDefElement in layoutState.Elements("LayoutDefinition")) { var layoutDef = LayoutDefinition.LoadFromState(layoutDefElement, this.viewCreators); if (layoutDef.Id.Equals(Guid.Empty)) { // Could be from a state format before layouts had ID's. Match the name and document affinity to the // default set, and if found, assume its ID. Failing that, give it a fresh one. var defaultLayoutDef = this.defaultLayoutDefinitions.FirstOrDefault(ld => ld.Header == layoutDef.Header && ld.DocumentFactoryName == layoutDef.DocumentFactoryName); if (defaultLayoutDef != null && !usedIds.Contains(defaultLayoutDef.Id)) { layoutDef.Id = defaultLayoutDef.Id; } else { layoutDef.Id = Guid.NewGuid(); } } // We only do this to guard against users having same-named layouts (affinitized with the same document factory). We // trust that Guid.NewGuid actually creates unique values... usedIds.Add(layoutDef.Id); if (layoutDef.DocumentFactoryName == null) { // This is a "global" (any-document) layout, so we can add it now. this.LayoutDefinitions.Add(layoutDef); } else { List <LayoutDefinition> list; // Stage this for validation against layout version if (!loadedLayoutDefs.TryGetValue(layoutDef.DocumentFactoryName, out list)) { list = new List <LayoutDefinition>(); loadedLayoutDefs.Add(layoutDef.DocumentFactoryName, list); } list.Add(layoutDef); } } foreach (var kvp in loadedLayoutDefs) { if (bumpedDocumentFactories.Contains(kvp.Key)) { var oldSet = kvp.Value.ToDictionary(ld => ld.Id); // These need to be updated/upgraded. We do that giving preference to the defaults by loading them first, // but keep their names in case the user has changed them. Then add the remaining loaded layouts. foreach (var layout in this.defaultLayoutDefinitions.Where(ld => ld.DocumentFactoryName == kvp.Key)) { LayoutDefinition existingLayout; if (oldSet.TryGetValue(layout.Id, out existingLayout)) { layout.Header = existingLayout.Header; oldSet.Remove(layout.Id); } // Mark this layout as having been reverted to default. This prevents the per-instance state data // from being loaded, which would likely fail due to structure mismatch with the definition. layout.RevertedToDefault = true; this.LayoutDefinitions.Add(layout); } // Get the rest -- don't just iterate over the dictionary, use the list instead to // preserve order (as much as possible). Note that these are not reverted to default, as // they do not have one. foreach (var remainingLayout in kvp.Value.Where(ld => oldSet.ContainsKey(ld.Id))) { this.LayoutDefinitions.Add(remainingLayout); } } else { // These are good to go foreach (var layout in kvp.Value) { this.LayoutDefinitions.Add(layout); } } } if (mainWindowState != null) { this.mainWindow.LoadWindowState(mainWindowState); } var windowElements = windowStateRoot.Elements("Window"); if (windowElements.Any()) { if (this.mainWindow.IsLoaded) { RecreateAuxiliaryWindows(windowElements); } else { RoutedEventHandler handler = null; handler = (s, e) => { RecreateAuxiliaryWindows(windowElements); this.mainWindow.Loaded -= handler; this.mainWindow.Activate(); }; this.mainWindow.Loaded += handler; } } }