문제

Windows Forms 응용 프로그램의로드 / 저장 기능을 구현하려고합니다.

다음 구성 요소가 있습니다.

  • 나무 뷰
  • 몇 가지 목록보기
  • 몇 개의 텍스트 상자
  • 몇 가지 물체 (큰 사전리스트를 보유한)

이 모든 것을 파일에 저장하고 나중에 이력서/로드하는 방법을 구현하고 싶습니다.

이것을하는 가장 좋은 방법은 무엇입니까?

XML 직렬화는 갈 길이라고 생각하지만 어떻게 또는 어디서 시작 해야할지 잘 모르겠습니다. 아니면이 작업을 수행하려면 정말 복잡한 솔루션이 필요합니까?

도움이 되었습니까?

해결책

다음은 물체와 일부 조상을 UI에 묶는 예입니다. 여기에서 C# 3.0을 사용하는 것은 순전히 간결합니다. 모든 것이 C# 2.0에서도 작동합니다.

여기의 대부분의 코드는 양식을 설정하거나 속성 변경 알림을 처리하는 것입니다. 중요한 것은 객체 모델에서 UI를 업데이트하는 데 전념하는 코드 또는 UI의 객체 모델이 없습니다.

또한 IDE는 바인딩 소스를 양식에 삭제하고 속성 그리드의 대화 상자를 통해 유형으로 데이터 소스를 설정하여 많은 데이터 바인딩 코드를 수행 할 수 있습니다.

속성 변경 알림 (속성 변경 사항)을 제공하는 것은 필수적이지 않습니다. 그러나 대부분의 2 방향 UI 바인딩은이를 구현하면 상당히 더 잘 작동합니다. 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));
    }
}

다른 팁

이상적으로는 UI 상태를 지속해서는 안됩니다. 데이터를 나타내는 일부 객체 모델의 상태를 지속해야합니다. 제외한 TreeView, 데이터 바인딩을 사용하여 객체 모델을 UI에 묶는 것은 상당히 사소합니다. 이것은 둘 중 하나 일 수 있습니다 DataTable-기반 접근법 또는 사용자 정의 클래스 계층 구조 (내 선호도).

UI에서 데이터를 분리 한 후에는 데이터 저장이 간단합니다. 많은 예가 있습니다 XmlSerializer 등.

예,이를 위해 XML 직렬화를 반드시 사용해야합니다. 그러나 Marc Gravell이 지적했듯이 GUI 구성 요소에 표시된 데이터를 먼저 보유하는 객체가 있어야합니다. 그런 다음 최소한의 코드 라인으로 실제로 (DE) 직렬화를 자동으로 만들 수 있습니다.

위의 샘플에는 문제가 있습니다. 결국 응용 프로그램이 업데이트된다고 생각하십시오. 객체 모델은 급격히 변할 수 있으므로 사막화 될 수 없습니다. XML 버전 1의 사막화가 버전 2에서 객체 모델로 사막화 될 수 있도록 할 수있는 일이 있지만 XML의 구조적 변화를 가질 가능성이있는 경우 XML Desorialization은 다음과 같습니다. ~ 아니다 가는 길.

이 경우 신청서가 고객에게 배포 된 경우 저장/로드 로직을 자세히 살펴 보는 것이 좋습니다.

버전화 된 직렬화/사제화
객체 상태를 다음의 형태로 직렬화하십시오.

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

이제 저장된 상태를 생성 한 객체 모델의 버전이 있습니다. 당신의 사막화에서 당신은이 사실을 위해 특별한 측정을 수행 할 수 있습니다. 예를 들어 다른 객체의 목록에 Field1- 값을 씁니다.

또 다른 접근법은 다음과 같습니다.

버전화 된 직렬화 및 변환 사제 화 전에
위에서 언급 한대로 객체 상태를 직렬화하십시오 (버전 속성 포함).
버전 속성이 버전이 아닌 경우 버전 속성을 살펴보면 직렬화 된 객체 상태를 XSL- 스크립트 또는 C# 코드로 현재 버전으로 변환 할 것으로 예상됩니다. 현재 프로젝트에서 XSL 변환 목록을 저장할 수 있습니다.

- conversions
    - v1-v2
    - v2-v3

현재 버전 3에 있고 XML 파일을로드하려면 버전 속성을보고 모든 XSL 스크립트를 실행하여 현재 버전 (버전 3)을 얻으십시오. 따라서 XSL 스크립트 V1-V2를 실행 한 후 V2-V3을 실행합니다.

이 경우 거꾸로 기능에 신경 쓰지 말아야 할 정상 직렬화 및 사막화 클래스를 가질 수 있습니다.

데이터 바인딩을 사용하여 객체 모델을 UI에 묶는 것은 상당히 사소합니다.

영구 스토리지없이 GUI 컨트롤이있는 객체를 어떻게 묶을 수 있습니까? 수동으로 수행하면 메모리의 모든 단일 객체에 대해 말도 안되는 양의 코드를 작성해야합니다. 이미이 데이터에 대한 일종의 클래스 스토리지가 있지만 정렬 시나리오가 바인드되어 있지 않습니다. 여기 에서이 글을 읽는 것과 같습니다.

직렬화 된 XML을로드하고 객체를 얻은 다음 개체를 읽고 전체 GUI를 채우는 로더를 작성해야합니까? 분명히 이것은 수동 로딩과 비슷합니다. 내가 뭔가를 놓치고 있습니까?

다음은 A 클래스 또는 구조물 직렬화 가능한 방법에 대한 훌륭한 기사입니다. 원하는 모든 데이터를 저장할 수있는 클래스를 만들 것입니다. 수업을 일련의 가능하게 만듭니다. 이 방법으로 몇 줄의 코드로 모든 데이터를 파일에 저장할 수 있습니다. 그런 다음 몇 줄의 코드 라인만으로 파일에서 데이터를 검색 할 수 있습니다.

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

클래스 직렬화의 대안은 XML 파일로 지속되는 시설에 내장 된 데이터 스토리지에 ADO.NET 데이터 세트를 사용하는 것입니다. 코드는 최소화되며 수행중인 작업 모델에 맞는 테이블을 설계하여 필요한 관련 데이터 만 저장할 수 있습니다. 또한 나중에 UI 상태를 로컬 파일 대신 데이터베이스에 유지하기로 결정한 경우 동일한 코드를 사용할 수 있습니다. 데이터 세트를 저장하려면 대체 기능 만 있으면됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top