Каков способ реализации функциональности сохранения / загрузки?

StackOverflow https://stackoverflow.com/questions/348022

  •  20-08-2019
  •  | 
  •  

Вопрос

Я пытаюсь реализовать функцию загрузки / сохранения для приложения Windows Forms.

У меня есть следующие компоненты:

  • Древовидный вид
  • Пара просмотров списка
  • Пара текстовых полей
  • Пара объектов (которые содержат большой список терминов)

Я хочу реализовать способ сохранить все это в файл и возобновить / загрузить его позже.

Каков наилучший способ сделать это?

Я думаю, что сериализация XML - это правильный путь, но я не совсем уверен, как и с чего начать.Или для этого потребуется действительно сложное решение?

Это было полезно?

Решение

Вот пример, который привязывает объект и некоторых предков к пользовательскому интерфейсу;использование C # 3.0 здесь исключительно для краткости - все будет работать и с C # 2.0.

Большая часть кода здесь предназначена для настройки формы и / или обработки уведомлений об изменении свойств - важно отметить, что нет никакого кода, посвященного обновлению пользовательского интерфейса из объектной модели или объектной модели из пользовательский интерфейс.

Обратите также внимание, что среда IDE может выполнять большую часть кода привязки данных для вас достаточно просто поместить BindingSource в форму и присвоить источнику данных тип с помощью диалогового окна в таблице свойств.

Обратите внимание, что нет необходимости предоставлять изменения свойств уведомления (материал PropertyChanged) - однако, большинство двухсторонних привязок пользовательского интерфейса будут работать значительно лучше если вы это реализуете.Не то чтобы у PostSharp было что-то подобное интересные способы сделать это с минимальным количеством кода.

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

Другие советы

В идеале, вы не должны сохранять состояние пользовательского интерфейса;вы должны сохранять состояние некоторой объектной модели, представляющей ваши данные.За исключением TreeView, довольно тривиально использовать привязку данных для привязки объектной модели к пользовательскому интерфейсу.Это может быть либо DataTableоснованный на подходе или пользовательской иерархии классов (мои предпочтения).

После того как вы отделили данные от пользовательского интерфейса, сохранить их очень просто.Существует множество примеров для XmlSerializer и т.д.

Да, вам определенно следует использовать для этого сериализацию XML.Но, как отметил Марк Гравелл, сначала у вас должны быть объекты, содержащие данные, отображаемые вашими компонентами графического интерфейса.Тогда вы можете практически сделать (де) сериализацию автоматической, затратив минимум строк кода.

существует проблема с приведенным выше образцом.учтите, что в конечном итоге ваше приложение будет обновлено.ваша объектная модель может кардинально измениться, и поэтому она не сможет быть десериализована.есть некоторые вещи, которые вы могли бы сделать, чтобы гарантировать, что десериализация из xml версии 1 может быть десериализована в вашу объектную модель в версии 2, но если есть вероятность, что вы можете внести большие структурные изменения, десериализация xml - это не путь, по которому нужно идти.

если это так, и ваше приложение развернуто для клиентов, я бы настоятельно рекомендовал подробнее ознакомиться с вашей логикой сохранения / загрузки.

Версионная Сериализация / Десериализация
сериализуйте состояние вашего объекта в виде:

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

итак, теперь у вас есть версия объектной модели, которая сгенерировала сохраненное состояние.при вашей десериализации вы можете выполнить специальные измерения, чтобы учесть этот факт.например, запишите значение Field1 в список в каком-нибудь другом объекте.

другим подходом было бы:

Версионная сериализация и преобразование перед десериализацией
сериализуйте состояние вашего объекта, как упоминалось выше (с атрибутом версии).
при десериализации посмотрите на атрибут version, если это не та версия, которую вы ожидаете, преобразуйте сериализованное состояние объекта с помощью xsl-скриптов или кода c # в вашу текущую версию.вы можете сохранить список преобразований xsl в вашем текущем проекте

- conversions
    - v1-v2
    - v2-v3

если вы в настоящее время используете версию 3 и хотите загрузить свой xml-файл, посмотрите на атрибут version и запустите все xsl-скрипты, чтобы получить вашу текущую версию (version 3).таким образом, вы бы запустили xsl-script v1-v2, а затем v2-v3.

в этом случае у вас могут быть обычные классы сериализации и десериализации, которые не должны заботиться о возможности обратной передачи.

довольно тривиально использовать привязку данных для привязки объектной модели к пользовательскому интерфейсу.

Как я могу связать объект с элементом управления GUI без постоянного хранилища?Если я делаю это вручную, это означает, что мне приходится писать смехотворное количество кода для каждого отдельного объекта в памяти.У меня уже есть какое-то хранилище классов для этих данных, но это не сценарий сортировки по привязке, это как прочитать эту запись здесь.

Должен ли я написать загрузчик, который загружает сериализованный XML и получает объект, а затем считывает объект и заполняет весь графический интерфейс?Очевидно, что это больше похоже на ручную загрузку, не являющуюся обязательной.Я что - то упускаю ?

Вот отличная статья о том, как сделать ваш класс или структуру сериализуемыми.Я бы создал класс, который позволит вам хранить все данные, которые вы пожелаете.Сделайте класс сериализуемым.Таким образом, всего за несколько строк кода вы можете сохранить все ваши данные в файл.Затем с помощью всего лишь нескольких строк кода вы можете извлечь данные из файла.

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

Альтернативой сериализации ваших классов является использование an ADO.NET Dataset для хранения данных, который имеет встроенные средства для сохранения в XML-файле.Код будет минимальным, и вы сможете хранить только необходимые вам релевантные данные, создав таблицы, соответствующие модели выполняемой вами операции.Кроме того, вы сможете использовать тот же код, если позже решите сохранить состояние пользовательского интерфейса в базе данных вместо локального файла.Вам понадобится только альтернативная функция для сохранения набора данных.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top