Frage

Ich versuche, eine Load / Save-Funktion zu implementieren für ein Windows Forms-Anwendung.

Ich habe folgende Komponenten bekommen:

  • Ein Baumansicht
  • Ein paar Listenansichten
  • Ein paar Textfelder
  • Ein paar Objekte (die einen großen Dictionary hält)

Ich möchte eine Art und Weise implementieren, dies alles in eine Datei, und wieder aufnehmen / laden Sie es später zu speichern.

Was ist der beste Weg, dies zu tun?

ich denke, XML-Serialisierung ist der Weg zu gehen, aber ich bin mir nicht ganz sicher, wie oder wo ich anfangen soll. Oder wird es eine wirklich komplexe Lösung in der Lage sein erfordern, dies zu tun?

War es hilfreich?

Lösung

Hier ist ein Beispiel, das ein Objekt und einige Vorfahren bindet an die UI; die Verwendung von C 3.0 # hier ist rein aus Gründen der Kürze - alles wäre 2.0 auch mit C # arbeiten.

Die meiste Code hier setzt das Formular oben, und / oder Umgang mit Immobilien-Änderungsbenachrichtigungen - wichtiger ist, gibt es keinen Code gewidmet Aktualisierung die Benutzeroberfläche von dem Objektmodell oder das Objektmodell aus die Benutzeroberfläche.

Beachten Sie auch die IDE kann eine Menge der Datenbindungscode tun für Sie, indem Sie einfach einen Binding auf den Abwurf bilden und die Einstellung der Datasource auf eine Art über der Dialog im Eigenschaftenraster.

Beachten Sie, dass es nicht wesentlich ist, Eigenschaftsänderung zu schaffen, Benachrichtigungen (die Property Sachen) - jedoch die meisten 2-Wege-UI funktioniert Bindung deutlich besser wenn Sie dies tun implementieren. Nicht, dass Postsharp hat einige interessante Möglichkeiten, dies mit minimalem Code zu tun.

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));
    }
}

Andere Tipps

Im Idealfall sollten Sie nicht den UI Zustand werden persistierende; Sie sollten den Zustand eines Objekts Modell werden persistierende Ihre Daten darstellen. Mit Ausnahme von TreeView, ist es ziemlich trivial Datenbindung zu verwenden, um ein Objekt-Modell an die UI zu binden. Dies könnte entweder ein DataTable basierten Ansatz oder eine benutzerdefinierte Klassenhierarchie (meine Vorliebe) sein.

Wenn Sie Daten von UI getrennt haben, Daten zu speichern ist einfach. Es gibt viele Beispiele für XmlSerializer etc.

Ja, Sie sollten auf jeden Fall die XML-Serialisierung für diese. Aber wie Marc GRA erwähnt, müssen Sie Objekte, die die angezeigten Daten von Ihren GUI-Komponenten zunächst halten. Dann können Sie praktisch machen (de) Serialisierung automatisch, mit minimalen Code-Zeilen.

gibt es ein Problem mit der Probe oben. bedenken, dass schließlich die Anwendung aktualisiert wird. Ihr Objektmodell könnte drastisch ändern und daher ist es nicht deserialisiert werden könnte. gibt es einige Dinge, die Sie tun können, um sicherzustellen, dass eine Deserialisierung von XML-Version 1 kann 2 bis Ihr Objektmodell in Version deserialisiert werden, aber wenn es die Möglichkeit, dass Sie große strukturelle Veränderungen xml Deserialisierung haben kann, ist nicht der Weg zu gehen.

Wenn dies der Fall ist und Ihre Anwendung für die Kunden im Einsatz i nachdrücklich empfohlen, einen weiteren Blick auf Ihre Speichern / Laden Logik nehmen würde vorschlagen.

versioniert Serialisierung / Deserialisierung
serialisiert Ihren Objektzustand in Form von:

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

So, jetzt haben Sie die Version des Objektmodells, das den gespeicherten Zustand erzeugt. in Ihrer Deserialisierung können Sie spezielle Messungen nehmen für diese Tatsache zu empfangen. zum Beispiel schreibt den Field1-Wert in einer Liste in einem anderen Objekt.

Ein weiterer Ansatz wäre:

versioniert Serialisierung und Conversion vor Deserialisierung
serialisiert Ihren Objektzustand, wie oben (mit einem Versionsattribut) erwähnt.
wenn Blick auf dem Versionsattribut Deserialisieren wenn dies nicht die Version, die Sie den serialisierte Objektzustand mit xsl-Skripten oder C # -Code, um Ihre aktuellen Version konvertieren erwarten. Sie können eine Liste von xsl Konvertierungen in Ihrem aktuellen Projekt speichern

- conversions
    - v1-v2
    - v2-v3

Wenn Sie derzeit in der Version 3 sind und mögen, dass Ihre XML-Datei sehen Sie das Versionsattribut laden und alle xsl Skripte ausführen, um Ihre aktuelle Version (Version 3) zu erhalten. so würden Sie xsl-Skript v1-v2 und danach v2-v3 ausführen.

in diesem Fall können Sie normale Serialisierung und Deserialisierung Klassen, die über rückwärts Fähigkeit nicht kümmern muss.

  

Es ist ziemlich trivial zu verwenden   Datenbindung ein Objektmodell zu binden, um   die Benutzeroberfläche.

Wie kann ich ein Objekt mit einer GUI-Steuerung ohne persistente Speicher binden? Wenn ich es manuell tun bedeutet, dass ich lächerlich Menge an Code für jedes einzelne Objekt im Speicher zu schreiben. Ich habe bereits für diese Daten eine Art von Klasse Speicher haben, aber es ist nicht binden Art Szenario, es ist wie das hier schreiben lesen.

Soll ich einen Loader schreiben, die serialisierte XML lädt und das Objekt und dann das Objekt gelesen und die gesamte GUI füllen? Offensichtlich ist dies eher wie die manuelle Beladung nicht bindend. Bin ich etwas fehlt?

Hier ist ein großer Artikel, wie Sie Ihre eine Klasse oder Struktur serializable zu machen. Ich würde eine Klasse erstellen, die Sie alle Daten, die Sie möchten, speichern können. Machen Sie die Klasse serialable. Auf diese Weise in nur wenigen Zeilen Code Sie alle Ihre Daten in einer Datei speichern kann. Dann mit nur ein paar Zeilen Code, um die Daten aus der Datei abrufen kann.

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

Eine Alternative Klassen zu serialisiert ist, die ein ADO.NET-Datensatz für die Datenspeicherung zu verwenden, die in Einrichtungen für persistierende in eine XML-Datei erstellt hat. Der Code wird minimal sein, und Sie können Sie durch die Gestaltung Tabellen müssen nur die relevanten Daten speichern, die das Modell der Operation passen Sie ausführen. Darüber hinaus werden Sie in der Lage sein, den gleichen Code zu verwenden, wenn Sie später entscheiden, um den UI-Zustand in einer Datenbank zu bestehen statt einer lokalen Datei. Sie würden nur eine alternative Funktion haben müssen, den Datensatz zu speichern.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top