public void SaveState(ShellViewModel shell, ShellView shellView, string fileName) { FileStream stream = null; try { stream = new FileStream(fileName, FileMode.Create, FileAccess.Write); using (var writer = new BinaryWriter(stream)) { stream = null; IEnumerable <LayoutItem> itemStates = shell.Items.Concat(shell.Widgets.Cast <LayoutItem>()); int itemCount = 0; // reserve some space for items count, it'll be updated later writer.Write(itemCount); foreach (var item in itemStates) { if (!item.ShouldReopenOnStart) { continue; } var itemType = item.GetType(); List <ExportAttribute> exportAttributes = itemType .GetCustomAttributes(typeof(ExportAttribute), false) .Cast <ExportAttribute>().ToList(); var layoutType = typeof(LayoutItem); // get exports with explicit types or names that inherit from ILayoutItem var exportTypes = (from att in exportAttributes // select the contract type if it is of type ILayoutitem. else null let typeFromContract = att.ContractType != null && layoutType.IsAssignableFrom(att.ContractType) ? att.ContractType : null // select the contract name if it is of type ILayoutItem. else null let typeFromQualifiedName = GetTypeFromContractNameAsILayoutItem(att) // select the viewmodel tpye if it is of type ILayoutItem. else null let typeFromViewModel = layoutType.IsAssignableFrom(itemType) ? itemType : null // att.ContractType overrides att.ContractName if both are set. // fall back to the ViewModel type of neither are defined. let type = typeFromContract ?? typeFromQualifiedName ?? typeFromViewModel where type != null select type).ToList(); // throw exceptions here, instead of failing silently. These are design time errors. var firstExport = exportTypes.FirstOrDefault(); if (firstExport == null) { throw new InvalidOperationException(string.Format( "A ViewModel that participates in LayoutItem.ShouldReopenOnStart must be decorated with an ExportAttribute who's ContractType that inherits from ILayoutItem, infringing type is {0}.", itemType)); } if (exportTypes.Count > 1) { throw new InvalidOperationException(string.Format( "A ViewModel that participates in LayoutItem.ShouldReopenOnStart can't be decorated with more than one ExportAttribute which inherits from ILayoutItem. infringing type is {0}.", itemType)); } var selectedTypeName = firstExport.AssemblyQualifiedName; if (string.IsNullOrEmpty(selectedTypeName)) { throw new InvalidOperationException(string.Format( "Could not retrieve the assembly qualified type name for {0}, most likely because the type is generic.", firstExport)); } // TODO: it is possible to save generic types. It requires that every generic parameter is saved, along with its position in the generic tree... A lot of work. writer.Write(selectedTypeName); writer.Write(item.ContentId); // Here's the tricky part. Because some items might fail to save their state, or they might be removed (a plug-in assembly deleted and etc.) // we need to save the item's state size to be able to skip the data during deserialization. // Save current stream position. We'll need it later. long stateSizePosition = writer.BaseStream.Position; // Reserve some space for item state size writer.Write(0L); long stateSize; try { long stateStartPosition = writer.BaseStream.Position; item.SaveState(writer); stateSize = writer.BaseStream.Position - stateStartPosition; } catch { stateSize = 0; } // Go back to the position before item's state and write the actual value. writer.BaseStream.Seek(stateSizePosition, SeekOrigin.Begin); writer.Write(stateSize); if (stateSize > 0) { // Got to the end of the stream writer.BaseStream.Seek(0, SeekOrigin.End); } itemCount++; } writer.BaseStream.Seek(0, SeekOrigin.Begin); writer.Write(itemCount); writer.BaseStream.Seek(0, SeekOrigin.End); shell.SaveLayout(writer.BaseStream); } } catch { if (stream != null) { stream.Dispose(); } } }
public void LoadState(ShellViewModel shell, ShellView shellView, string fileName) { var layoutItems = new Dictionary <string, LayoutItem>(); if (!File.Exists(fileName)) { return; } FileStream stream = null; try { stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); using (var reader = new BinaryReader(stream)) { stream = null; int count = reader.ReadInt32(); for (int i = 0; i < count; i++) { string typeName = reader.ReadString(); string contentId = reader.ReadString(); long stateEndPosition = reader.ReadInt64(); stateEndPosition += reader.BaseStream.Position; var contentType = Type.GetType(typeName); bool skipStateData = true; if (contentType != null) { var contentInstance = IoC.GetInstance(contentType, null) as LayoutItem; if (contentInstance != null) { layoutItems.Add(contentId, contentInstance); try { contentInstance.LoadState(reader); skipStateData = false; } catch { skipStateData = true; } } } // Skip state data block if we couldn't read it. if (skipStateData) { reader.BaseStream.Seek(stateEndPosition, SeekOrigin.Begin); } } shell.LoadLayout(reader.BaseStream, shell.ShowTool, shell.OpenDocument, layoutItems); } } catch { if (stream != null) { stream.Close(); } } }