| File: System\ComponentModel\Design\Serialization\LocalizationCodeDomSerializer.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.CodeDom; using System.Resources; namespace System.ComponentModel.Design.Serialization; /// <summary> /// Code model serializer for resource managers. This is called /// in one of two ways. On Deserialization, we are associated /// with a ResourceManager object. Instead of creating a /// ResourceManager, however, we create an object called a /// SerializationResourceManager. This class inherits /// from ResourceManager, but overrides all of the methods. /// Instead of letting resource manager maintain resource /// sets, it uses the designer host's IResourceService /// for this purpose. /// /// During serialization, this class will also create /// a SerializationResourceManager. This will be added /// to the serialization manager as a service so other /// resource serializers can get at it. SerializationResourceManager /// has additional methods on it to support writing data /// into the resource streams for various cultures. /// </summary> internal class LocalizationCodeDomSerializer : CodeDomSerializer { private readonly CodeDomLocalizationModel _model; private readonly CodeDomSerializer? _currentSerializer; /// <summary> /// Only we can create an instance of this. Everyone else accesses it though /// static properties. /// </summary> internal LocalizationCodeDomSerializer(CodeDomLocalizationModel model, object currentSerializer) { _model = model; _currentSerializer = currentSerializer as CodeDomSerializer; } /// <summary> /// Returns true if we should emit an ApplyResources method for this object. We only emit /// this method once during serialization, and we track this by appending an object to /// the context stack. /// </summary> private static bool EmitApplyMethod(IDesignerSerializationManager manager, object owner) { ApplyMethodTable? table = (ApplyMethodTable?)manager.Context[typeof(ApplyMethodTable)]; if (table is null) { table = new(); manager.Context.Append(table); } return table.Add(owner); } /// <summary> /// Serializes the given object into a CodeDom object. This uses the stock /// resource serialization scheme and retains the expression it provides. /// </summary> public override object? Serialize(IDesignerSerializationManager manager, object value) { PropertyDescriptor? descriptor = (PropertyDescriptor?)manager.Context[typeof(PropertyDescriptor)]; ExpressionContext? tree = (ExpressionContext?)manager.Context[typeof(ExpressionContext)]; #pragma warning disable SYSLIB0050 // Type or member is obsolete bool isSerializable = value is null || GetReflectionTypeHelper(manager, value).IsSerializable; #pragma warning restore SYSLIB0050 // If value is not serializable, we have no option but to call the original serializer, // since we cannot push this into resources. bool callExistingSerializer = !isSerializable; // Compat: If we are serializing content, we need to skip property reflection to preserve compatibility, // since tools like WinRes expect items in collections (like TreeNodes and ListViewItems) // to be serialized as binary blobs. bool serializingContent = (descriptor is not null && descriptor.Attributes.Contains(DesignerSerializationVisibilityAttribute.Content)); // We also skip back to the original serializer if there is a preset value for this object. if (!callExistingSerializer) { callExistingSerializer = tree is not null && tree.PresetValue is not null && tree.PresetValue == value; } if (_model == CodeDomLocalizationModel.PropertyReflection && !serializingContent && !callExistingSerializer) { // For a property reflecting model, we need to do more work. Here we need to find // the object we are serializing against and inject an "ApplyResources" method // against the object and its name. If any of this machinery fails we will // just return the existing expression which will default to the original behavior. CodeStatementCollection? statements = (CodeStatementCollection?)manager.Context[typeof(CodeStatementCollection)]; // In the case of extender properties, we don't want to serialize using the property // reflecting model. In this case we'll skip it and fall through to the // property assignment model. bool skipPropertyReflect = false; if (descriptor is not null) { ExtenderProvidedPropertyAttribute? attribute = descriptor.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; if (attribute is not null && attribute.ExtenderProperty is not null) { skipPropertyReflect = true; } } if (!skipPropertyReflect && tree is not null && statements is not null) { string? name = manager.GetName(tree.Owner); CodeExpression? ownerExpression = SerializeToExpression(manager, tree.Owner); if (name is not null && ownerExpression is not null) { RootContext? rootContext = manager.Context[typeof(RootContext)] as RootContext; if (rootContext is not null && rootContext.Value == tree.Owner) { name = "$this"; } // Ok, if we got here it means we have enough data to emit // using the reflection model. SerializeToResourceExpression(manager, value, false); if (EmitApplyMethod(manager, tree.Owner)) { ResourceManager? resourceManager = manager.Context[typeof(ResourceManager)] as ResourceManager; Debug.Assert(resourceManager is not null, "No resource manager available in context."); CodeExpression? rmExpression = GetExpression(manager, resourceManager); Debug.Assert(rmExpression is not null, "No expression available for resource manager."); CodeMethodInvokeExpression methodInvoke = new() { Method = new CodeMethodReferenceExpression(rmExpression, "ApplyResources") }; methodInvoke.Parameters.Add(ownerExpression); methodInvoke.Parameters.Add(new CodePrimitiveExpression(name)); statements.Add(methodInvoke); } return null; // we have already worked our statements into the tree. } } } return callExistingSerializer ? _currentSerializer?.Serialize(manager, value!) : SerializeToResourceExpression(manager, value); } /// <summary> /// This class is used as a table to track which objects we've injected the "ApplyResources" method for. /// </summary> private class ApplyMethodTable { private readonly HashSet<object> _table = []; internal bool Add(object value) => _table.Add(value); } }