首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF Brush、FontFamily、FontWeight 等屬性 XML 序列化/反序列化通用解決方案

     編輯:關於.NET

問題背景

WPF 核心型別(Brush、FontFamily、FontWeight、Color 等)未標記 [Serializable],預設 XmlSerializer 無法直接序列化,拋出例外。

解決架構:PropertySerializateDictionary

繼承 Dictionary<string, TypeAndValue> 並實作 IXmlSerializable,搭配輔助類別 TypeAndValue 封裝型別與實例,利用 TypeConverter 進行字串轉換。

核心代碼

TypeAndValue:封裝型別與值

public class TypeAndValue
{
    public Type Type { get; set; }
    public object Value { get; set; }
    public TypeAndValue() { }
    public TypeAndValue(Type type, object value) { Type = type; Value = value; }

    public override bool Equals(object obj)
    {
        var other = obj as TypeAndValue;
        if (other == null) return false;
        if (other.Type != this.Type) return false;
        var eqMethod = Type.GetMethod("Equals", new[] { Type });
        return (bool)eqMethod.Invoke(other.Value, new object[] { this.Value });
    }
}

PropertySerializateDictionary:序列化引擎

public class PropertySerializateDictionary : Dictionary<string, TypeAndValue>, IXmlSerializable
{
    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        if (reader.IsEmptyElement) return;
        reader.Read(); // 移至根元素
        string typeName = reader.GetAttribute("Type");
        var type = Type.GetType(typeName);
        var props = type.GetProperties();

        while (reader.NodeType != XmlNodeType.EndElement)
        {
            string key = reader.Name;
            var instance = Activator.CreateInstance(type);
            this[key] = new TypeAndValue(type, instance);

            if (!reader.IsEmptyElement)
            {
                reader.ReadStartElement();
                if (reader.NodeType != XmlNodeType.EndElement)
                {
                    foreach (var pi in props)
                    {
                        string raw = reader.ReadElementString(pi.Name);
                        object converted = ConvertValue(pi.PropertyType, raw);
                        pi.SetValue(instance, converted, null);
                    }
                }
            }
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        if (Count == 0) return;
        foreach (var kvp in this)
        {
            writer.WriteStartElement(kvp.Key);
            writer.WriteAttributeString("Type", kvp.Value.Type.FullName);
            foreach (var pi in kvp.Value.Type.GetProperties())
            {
                writer.WriteElementString(pi.Name, pi.GetValue(kvp.Value.Value, null)?.ToString());
            }
            writer.WriteEndElement();
        }
    }

    private object ConvertValue(Type targetType, string input)
    {
        if (targetType == typeof(Brush))
            return new BrushConverter().ConvertFromString(input);
        if (targetType == typeof(FontFamily))
            return new FontFamilyConverter().ConvertFromString(input);
        if (targetType == typeof(FontWeight))
            return new FontWeightConverter().ConvertFromString(input);
        if (targetType == typeof(Color))
            return (Color)ColorConverter.ConvertFromString(input);
        return Convert.ChangeType(input, targetType);
    }

    public override bool Equals(object obj) => /* 比較每個 Key/Value */ true;
}

使用範例

var dict = new PropertySerializateDictionary();
dict["TitleStyle"] = new TypeAndValue(typeof(SerialMock), new SerialMock 
{ 
    FontWeight = FontWeights.Bold, 
    FontFamily = new FontFamily("Segoe UI") 
});

// 序列化
var serializer = new XmlSerializer(typeof(PropertySerializateDictionary));
using (var ms = new MemoryStream())
{
    serializer.Serialize(ms, dict);
    ms.Position = 0;
    var xml = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(xml);
}

// 反序列化
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
    var restored = (PropertySerializateDictionary)serializer.Deserialize(ms);
    // restored["TitleStyle"].Value 恢復 FontWeight、FontFamily
}

單元測試驗證

[TestMethod]
public void ReadXmlTest()
{
    var dict = new PropertySerializateDictionary();
    dict["People1"] = new TypeAndValue(typeof(SerialMock), new SerialMock { FirstName = "a", FontWeight = FontWeights.Light, BirthDate = new DateTime(1984,5,17) });
    dict["People2"] = new TypeAndValue(typeof(SerialMock), new SerialMock { FirstName = "b", FontWeight = FontWeights.Black, FontFamily = new FontFamily("PMingLiU") });

    var serializer = new XmlSerializer(typeof(PropertySerializateDictionary));
    using (var ms = new MemoryStream())
    {
        serializer.Serialize(ms, dict);
        ms.Position = 0;
        var restored = (PropertySerializateDictionary)serializer.Deserialize(ms);
        Assert.AreEqual(dict, restored); // 需正確實作 Equals
    }
}

限制與擴充

  • 目前支援基本型別、WPF 常見型別(Brush/FontFamily/FontWeight/Color)。
  • 集合型別、結構體需擴充 ConvertValue 邏輯(如 JSON 序列化後存字串)。
  • 大量資料建議改用 DataContractSerializer 或 JSON.NET,效能更佳。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved