2008-12-07 13 views
5

Estoy tratando de implementar una función Cargar/Guardar para una aplicación de Windows Forms.¿Cuál es la manera de implementar la funcionalidad Guardar/Cargar?

que tengo siguientes componentes:

  • una vista de árbol
  • Un par de vistas de lista
  • Un par de cuadros de texto
  • Un par de objetos (que tiene un gran dictionarylist)

Quiero implementar una forma de guardar todo esto en un archivo, y reanudar/cargarlo más adelante.

¿Cuál es la mejor manera de hacerlo?

Creo que la serialización XML es el camino a seguir, pero no estoy muy seguro de cómo o por dónde empezar. ¿O requerirá una solución realmente compleja para poder hacer esto?

Respuesta

6

Aquí hay un ejemplo que vincula un objeto y algunos antepasados ​​ a la interfaz de usuario; el uso de C# 3.0 aquí es puramente breve - todo funcionaría también con C# 2.0.

La mayor parte del código aquí es la creación de la forma, y ​​/ o tratar con las notificaciones de cambio de propiedad - importante, no hay ningún código dedicado a la actualización de la interfaz de usuario del modelo de objetos o el modelo de objetos desde la interfaz de usuario.

Tenga en cuenta también el IDE puede hacer un montón de código de enlace de datos para usted, simplemente dejando caer una BindingSource en la forma y establecer el origen de datos a través de un tipo el cuadro de diálogo en la cuadrícula de propiedades.

Tenga en cuenta que no es esencial para proporcionar notificaciones de cambio de propiedad (las cosas PropertyChanged) - sin embargo, más de 2 vías IU unión funcionará mucho mejor si lo hace implementar esto. No es que PostSharp tenga algunas formas interesantes de hacerlo con un código mínimo.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.IO; 
using System.Windows.Forms; 
using System.Xml.Serialization; 
static class Program { // formatted for vertical space 
    [STAThread] 
    static void Main() { 
     Application.EnableVisualStyles(); 

     Button load, save, newCust; 
     BindingSource source = new BindingSource { DataSource = typeof(Customer) }; 
     XmlSerializer serializer = new XmlSerializer(typeof(Customer)); 
     using (Form form = new Form { 
      DataBindings = {{"Text", source, "Name"}}, // show customer name as form title 
      Controls = { 
       new DataGridView { Dock = DockStyle.Fill, // grid of orders 
        DataSource = source, DataMember = "Orders"}, 
       new TextBox { Dock = DockStyle.Top, ReadOnly = true, // readonly order ref 
        DataBindings = {{"Text", source, "Orders.OrderRef"}}}, 
       new TextBox { Dock = DockStyle.Top, // editable customer name 
        DataBindings = {{"Text", source, "Name"}}}, 
       (save = new Button { Dock = DockStyle.Bottom, Text = "save" }), 
       (load = new Button{ Dock = DockStyle.Bottom, Text = "load"}), 
       (newCust = new Button{ Dock = DockStyle.Bottom, Text = "new"}), 
      } 
     }) 
     { 
      const string PATH = "customer.xml"; 
      form.Load += delegate { 
       newCust.PerformClick(); // create new cust when loading form 
       load.Enabled = File.Exists(PATH); 
      }; 
      save.Click += delegate { 
       using (var stream = File.Create(PATH)) { 
        serializer.Serialize(stream, source.DataSource); 
       } 
       load.Enabled = true; 
      }; 
      load.Click += delegate { 
       using (var stream = File.OpenRead(PATH)) { 
        source.DataSource = serializer.Deserialize(stream); 
       } 
      }; 
      newCust.Click += delegate { 
       source.DataSource = new Customer(); 
      }; 
      Application.Run(form); 
     } 
    } 
} 

[Serializable] 
public sealed class Customer : NotifyBase { 
    private int customerId; 
    [DisplayName("Customer Number")] 
    public int CustomerId { 
     get { return customerId; } 
     set { SetField(ref customerId, value, "CustomerId"); } 
    } 

    private string name; 
    public string Name { 
     get { return name; } 
     set { SetField(ref name, value, "Name"); } 
    } 

    public List<Order> Orders { get; set; } // XmlSerializer demands setter 

    public Customer() { 
     Orders = new List<Order>(); 
    } 
} 

[Serializable] 
public sealed class Order : NotifyBase { 
    private int orderId; 
    [DisplayName("Order Number")] 
    public int OrderId { 
     get { return orderId; } 
     set { SetField(ref orderId, value, "OrderId"); } 
    } 

    private string orderRef; 
    [DisplayName("Reference")] 
    public string OrderRef { 
     get { return orderRef; } 
     set { SetField(ref orderRef, value, "OrderRef"); } 
    } 

    private decimal orderValue, carriageValue; 

    [DisplayName("Order Value")] 
    public decimal OrderValue { 
     get { return orderValue; } 
     set { 
      if (SetField(ref orderValue, value, "OrderValue")) { 
       OnPropertyChanged("TotalValue"); 
      } 
     } 
    } 

    [DisplayName("Carriage Value")] 
    public decimal CarriageValue { 
     get { return carriageValue; } 
     set { 
      if (SetField(ref carriageValue, value, "CarriageValue")) { 
       OnPropertyChanged("TotalValue"); 
      } 
     } 
    } 

    [DisplayName("Total Value")] 
    public decimal TotalValue { get { return OrderValue + CarriageValue; } } 
} 

[Serializable] 
public class NotifyBase { // purely for convenience 
    [field: NonSerialized] 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected bool SetField<T>(ref T field, T value, string propertyName) { 
     if (!EqualityComparer<T>.Default.Equals(field, value)) { 
      field = value; 
      OnPropertyChanged(propertyName); 
      return true; 
     } 
     return false; 
    } 
    protected virtual void OnPropertyChanged(string propertyName) { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 
+0

gracias por el código, parece que esto responde mi pregunta, intentaré implementar tal solución y veré si puedo tener éxito. He visto postsharp, pero no estoy seguro de si es una buena manera de hacerlo o no. –

+0

TreeViews son un dolor, nota –

6

Idealmente, no debería persistir el estado de la IU; Debería persistir en el estado de algún modelo de objetos que represente sus datos. Con la excepción de TreeView, es bastante trivial utilizar el enlace de datos para vincular un modelo de objetos a la interfaz de usuario. Esto podría ser un enfoque basado en DataTable o una jerarquía de clases personalizada (mi preferencia).

Una vez que haya separado los datos de la interfaz de usuario, guardar los datos es simple. Hay muchos ejemplos para XmlSerializer etc.

+0

No estoy muy seguro de cómo hacerlo, nunca utilicé el enlace de datos entre los controles y las clases (solo lo uso para almacenamiento persistente, configuraciones, etc.). Lo miraré. –

+0

¿Podría darme o señalar un ejemplo para este tipo de enlace entre objetos y controles? –

1

Sí, definitivamente debe usar la serialización XML para esto. Pero como notó Marc Gravell, primero debe tener objetos que contengan los datos mostrados por los componentes de su GUI. Entonces prácticamente puede hacer (de) serialización automática, con un mínimo de líneas de código.

0

es bastante trivial utilizar enlace de datos para atar un modelo de objetos a la interfaz de usuario.

¿Cómo puedo vincular un objeto con un control GUI sin un almacenamiento persistente? Si lo hago manualmente, eso significa que tengo que escribir una cantidad ridícula de código para cada objeto en la memoria. Ya tengo algún tipo de almacenamiento de clase para estos datos, pero no es un escenario de ordenamiento obligatorio, es como leer esto escribir aquí.

¿Se supone que debo escribir un cargador que cargue XML serializado y obtener el objeto y luego leer el objeto y completar toda la GUI? Obviamente esto es más parecido a la carga manual no vinculante. Me estoy perdiendo de algo ?

+0

Voy a poner un ejemplo juntos en el tren; se publicará en un par de horas. Lo siento por la demora: necesitaba dormir ;-p –

+0

gracias por el ejemplo, se ve muy bien. Lo cavaré También bastante curioso acerca de tu opinión sobre el uso de Postsharp para esto. –

0

Aquí hay un excelente artículo sobre cómo hacer que su clase o estructura sea serializable. Crearía una clase que te permitirá almacenar todos los datos que desees. Haz la clase convertible. De esta manera, en unas pocas líneas de código puede guardar todos sus datos en un archivo. Luego, con solo unas pocas líneas más de código, puede recuperar los datos del archivo.

http://www.codeproject.com/KB/cs/objserial.aspx

0

Una alternativa a la serialización de sus clases es utilizar el conjunto de datos ADO.NET para un almacenamiento de datos que se ha construido en las instalaciones por persistir en un archivo XML. El código será mínimo y podrá almacenar solo los datos relevantes que necesite diseñando tablas que se ajusten al modelo de la operación que está realizando.Además, podrá usar el mismo código si luego decide persistir en el estado de la IU en una base de datos en lugar de en un archivo local. Solo necesitaría tener una función alternativa para guardar el Dataset.

+0

Gracias por la idea, a menos que realmente la necesite, guardo todo en la memoria (problemas de rendimiento), sin embargo, creo que ADO.NET obtuvo algún tipo de soporte de estructuras en la memoria. –

1

Hay un problema con el ejemplo anterior. considere que eventualmente su aplicación se actualice. su modelo de objeto podría cambiar drásticamente y, por lo tanto, no podría deserializarse. hay algunas cosas que podría hacer para asegurar que una deserialización de la versión 1 de xml pueda deserializarse para su modelo de objeto en la versión 2, pero si existe la posibilidad de que pueda tener grandes cambios estructurales, la deserialización xml es , no el camino a seguir.

si este es el caso y su aplicación se implementa para los clientes, le recomiendo encarecidamente que eche un vistazo a su lógica de guardar/cargar.

Versionized serialización/deserialización
serializar el estado del objeto en forma de:

<ObjectState version="1"> 
    <Field1>value</Field1> 
    ... etc ... 
</ObjectState> 

por lo que ahora tiene la versión del modelo de objeto que generó el estado guardado. en su deserialización puede tomar medidas especiales para acomodarse a este hecho. por ejemplo, escriba Field1-Value en una lista en algún otro objeto.

otro enfoque sería:

Versionized serialización y la conversión antes Deserialización
serializar el estado del objeto como se mencionó anteriormente (con un atributo de versión).
al deserializar, mire el atributo versión si esta no es la versión que espera convertir el estado del objeto serializado con xsl-scripts o el código C# a su versión actual. puede guardar una lista de conversiones XSL en su proyecto actual

- conversions 
    - v1-v2 
    - v2-v3 

si usted está actualmente en la versión 3 y desea cargar su mirada archivo XML en el atributo de versión y ejecutar todos los scripts XSL para obtener su versión actual (versión 3).para que pueda ejecutar xsl-script v1-v2 y luego v2-v3.

En este caso puede tener clases normales de serialización y deserialización que no deben preocuparse por la capacidad de retroceder.

Cuestiones relacionadas