2010-07-19 9 views
33

Un problema me molestó lo suficiente como para registrar en stackoverflow. Actualmente si quiero serializar color a cadena XML como el color con nombre o #rrggbb o #aarrggbb, lo hago de esta manera:serialización xml más elegante de la estructura de color

[XmlIgnore()] 
public Color color; 

[XmlElement(ElementName = "Color")] 
public String color_XmlSurrogate 
{ 
    get { return MyColorConverter.SetColor(color); } 
    set { color = MyColorConverter.GetColor(value); } 
} 

Aquí MyColorConverter hace serialización como a mí me gusta. Pero todo esto se siente como un kludge, con campo adicional y todo. ¿Hay alguna manera de hacerlo funcionar en menos líneas, tal vez conectando TypeDescriptor con atributos C# relacionados con xml?

+0

Creo que ha llegado a los límites de la serialización de XML. Es posible que desee buscar en la serialización de DataContract en su lugar, – leppie

Respuesta

53

Aquí hay algo que estoy usando para serializar la estructura Color en XML. Es mejor que sombrear la propiedad primaria Color en mi opinión. Cualquier sugerencia bienvenida

La clase XmlColor se basa principalmente en la característica de lenguaje implicit operator para proporcionar las transformaciones de datos clave. Sin esto, la clase es básicamente inútil. Se agregaron otros bits de funcionalidad para completar la clase.

El asistente XmlColor también proporciona una manera conveniente de separar los componentes de color. Agregué la propiedad Alpha para mostrar esto. Observe el componente Alpha no será serializado si está acodado todo el camino hasta 255.

deserializar el valor de color Web combina el valor Alpha almacenada actualmente en la instancia. El orden en que se analizan los atributos no debería importar. Si falta el atributo Alpha en el origen XML, el valor del componente de la instancia se utilizará para establecer el nivel Alpha. Esto es discutiblemente defectuoso; Sin embargo, en el caso de serialización XML, la clase XmlColor será inicializado con Color.Black establecer el Alpha a 255.

estoy trabajando fuera del entorno de VS2010 y edificio contra .Net 4. No tengo idea de su grado de compatibilidad del código es con versiones anteriores.

Aquí es una propiedad de ejemplo que debe ser serializado a XML:

[XmlElement(Type=typeof(XmlColor))] 
    public Color MyColor { get; set; } 

Aquí está la clase XmlColor ayudante:

public class XmlColor 
{ 
    private Color color_ = Color.Black; 

    public XmlColor() {} 
    public XmlColor(Color c) { color_ = c; } 


    public Color ToColor() 
    { 
     return color_; 
    } 

    public void FromColor(Color c) 
    { 
     color_ = c; 
    } 

    public static implicit operator Color(XmlColor x) 
    { 
     return x.ToColor(); 
    } 

    public static implicit operator XmlColor(Color c) 
    { 
     return new XmlColor(c); 
    } 

    [XmlAttribute] 
    public string Web 
    { 
     get { return ColorTranslator.ToHtml(color_); } 
     set { 
      try 
      { 
       if (Alpha == 0xFF) // preserve named color value if possible 
        color_ = ColorTranslator.FromHtml(value); 
       else 
        color_ = Color.FromArgb(Alpha, ColorTranslator.FromHtml(value)); 
      } 
      catch(Exception) 
      { 
       color_ = Color.Black; 
      } 
     } 
    } 

    [XmlAttribute] 
    public byte Alpha 
    { 
     get { return color_.A; } 
     set { 
      if (value != color_.A) // avoid hammering named color if no alpha change 
       color_ = Color.FromArgb(value, color_); 
     } 
    } 

    public bool ShouldSerializeAlpha() { return Alpha < 0xFF; } 
} 
+2

¡Me gusta esto! Si funciona, eso es. Utilizo VS2008 con .NET 3.5, por lo que tendré que verificar esto en el futuro. ¿Hay alguna razón para usar ToColor/FromColor en lugar de alguna propiedad Color? ¿Se puede recodificar esto para tener solo una cadena xml como valor serializado? Me gustaría que este sea el resultado: ' #aarrggbb' – Dialecticus

+4

Puede quitar la clase a los operadores implícitos y una sola propiedad publicable. Cómo se serializa esa propiedad depende totalmente de usted. Noté que llamar a los métodos Argb da como resultado cadenas hexagonales independientemente del valor alfa. Elegí XmlAttribute para evitar elementos secundarios. Puede considerar un atributo XmlAtributo "ARGB" y forzar las cadenas hexagonales de 4 componentes utilizando los métodos Argb. Sin duda, puede exprimir una propiedad Color si lo encuentra útil. – bvj

+3

@bvj, lástima que no puedo darte una recompensa por esto :) –

4

Un dolor, ¿no? Eso es todo lo que puede hacer con XmlSerializer, a menos que implemente IXmlSerializable (que yo hago no recomendamos). Opciones:

  • palo con eso, sino que también marcará color_XmlSurrogate como [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] - que va a parar a aparecer en la mayoría de puntos de vista, el enlace de datos, y en el código-editor al hacer referencia a su ensamblaje en un DLL
  • uso DataContractSerializer, que apoya a las propiedades privadas (pero que no atributos XML de apoyo, no se puede ganar ...)

por cierto, tendría color como una propiedad, no un campo:

[XmlIgnore] 
public Color Color {get;set;} 
1

he encontrado otra solución,

Es posible utilizar System.Windows.Media.Color en lugar de System.Drawing.Color.
Se puede serializar a XML.

+0

Lo probé y por un valor de color obtuve varias etiquetas sobre las cuales no tengo control. Eso no es una serialización como yo quiero. – Dialecticus

+0

@Dialecticus, por favor explique. –

+0

para el color (A = 123, R = 45, G = 67, B = 89) este es el resultado de la serialización: ' 0,482352942 0,0262412224 0,0561284944 0.09989873 '. Lo que quiero como resultado es ' # 7B2D4359'. Y además, realmente necesito el color GDI, no el color de los medios, porque esa es la estructura con la que estoy trabajando. – Dialecticus

2

Dado que esta es mi primera pregunta aquí, decidí investigar más. @bvj dio una excelente respuesta. Ajusté su código, así que aquí está. XmlColor la clase se cambia para que la cadena serializada entre en el texto de la etiqueta, y hay funciones estáticas públicas si la cadena se debe serializar como atributo XML en lugar de etiqueta. Desafortunadamente, los atributos todavía deben ser serializados con campos sustitutos, pero por favor corrígeme si me equivoco. Utilización de la clase:

// Color as tag 
[XmlElement(Type = typeof(XmlColor))] 
public Color ColorAsTag { get; set; } 

// Color as attribute 
[XmlIgnore] 
public Color ColorAsAttribute { get; set; } 

[XmlAttribute("ColorAsAttribute")] 
public string ColorAsAttribute_XmlSurrogate 
{ 
    get { return XmlColor.FromColor(ColorAsAttribute); } 
    set { ColorAsAttribute = XmlColor.ToColor(value); } 
} 

La clase que lo hace posible:

public class XmlColor 
{ 
    private Color color_ = Color.Black; 

    public XmlColor() { } 
    public XmlColor(Color c) { color_ = c; } 


    public static implicit operator Color(XmlColor x) 
    { 
     return x.color_; 
    } 

    public static implicit operator XmlColor(Color c) 
    { 
     return new XmlColor(c); 
    } 

    public static string FromColor(Color color) 
    { 
     if (color.IsNamedColor) 
      return color.Name; 

     int colorValue = color.ToArgb(); 

     if (((uint)colorValue >> 24) == 0xFF) 
      return String.Format("#{0:X6}", colorValue & 0x00FFFFFF); 
     else 
      return String.Format("#{0:X8}", colorValue); 
    } 

    public static Color ToColor(string value) 
    { 
     try 
     { 
      if (value[0] == '#') 
      { 
       return Color.FromArgb((value.Length <= 7 ? unchecked((int)0xFF000000) : 0) + 
        Int32.Parse(value.Substring(1), System.Globalization.NumberStyles.HexNumber)); 
      } 
      else 
      { 
       return Color.FromName(value); 
      } 
     } 
     catch (Exception) 
     { 
     } 

     return Color.Black; 
    } 

    [XmlText] 
    public string Default 
    { 
     get { return FromColor(color_); } 
     set { color_ = ToColor(value); } 
    } 
} 
16

creo continuación tengo una solución más fácil a eso. La serialización en color se ignora y el color se guarda y se carga como datos ARGB simples de 32 bits.

[XmlIgnore] 
public Color BackColor { get; set; } 

[XmlElement("BackColor")] 
public int BackColorAsArgb 
{ 
    get { return BackColor.ToArgb(); } 
    set { BackColor = Color.FromArgb(value); } 
} 
+3

Esta solución aún adolece del problema que quería evitar, que es propiedad sustituta. – Dialecticus

+3

Tengo curiosidad por los motivos por los que desea evitar la "propiedad sustituta"? – jwize

Cuestiones relacionadas