5

Windows フォーム アプリケーションの読み込み/保存機能を実装しようとしています。

次のコンポーネントがあります。

  • ツリー ビュー
  • いくつかのリスト ビュー
  • いくつかのテキスト ボックス
  • いくつかのオブジェクト (大きな辞書リストを保持)

これをすべてファイルに保存し、後で再開/ロードする方法を実装したいと考えています。

これを行う最善の方法は何ですか?

XML シリアライゼーションが進むべき道だと思いますが、どのように、またはどこから始めればよいかよくわかりません。それとも、これを行うには非常に複雑なソリューションが必要ですか?

4

7 に答える 7

6

オブジェクトといくつかの祖先を UI にバインドする例を次に示します。ここで C# 3.0 を使用しているのは、単に簡潔にするためです。すべて C# 2.0 でも機能します。

ここにあるコードのほとんどは、フォームの設定、および/またはプロパティ変更通知の処理です。重要なのは、オブジェクト モデルからの UI の更新、または UI からのオブジェクト モデルの更新専用のコードがないことです。

IDE は、フォームに BindingSource をドロップし、プロパティ グリッドのダイアログを介して DataSource を型に設定するだけで、多くのデータ バインディング コードを実行できることにも注意してください。

プロパティ変更通知 (PropertyChanged など) を提供することは必須ではないことに注意してください。ただし、これを実装すると、ほとんどの双方向 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));
    }
}
于 2008-12-08T08:23:02.607 に答える
6

理想的には、UI の状態を永続化しないでください。データを表すオブジェクト モデルの状態を永続化する必要があります。を除いてTreeView、データ バインディングを使用してオブジェクト モデルを UI に結び付けるのは簡単です。これは、DataTableベースのアプローチ、またはカスタム クラス階層 (私の好み) のいずれかです。

UI からデータを分離したら、データの保存は簡単です。XmlSerializerなどの作例が豊富です。

于 2008-12-07T20:24:24.227 に答える
1

上記のサンプルに問題があります。最終的にアプリケーションが更新されることを考慮してください。オブジェクトモデルが大幅に変更される可能性があるため、逆シリアル化できませんでした。xmlバージョン1からのデシリアライズをバージョン2のオブジェクトモデルにデシリアライズできるようにするためにできることがいくつかありますが、大きな構造変更が発生する可能性がある場合は、xmlデシリアライズを実行する方法はありません

これが当てはまり、アプリケーションが顧客にデプロイされている場合は、保存/ロードロジックをさらに検討することを強くお勧めします。

バージョン化されたシリアル化/逆
シリアル化は、オブジェクトの状態を次の形式でシリアル化します。

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

これで、保存された状態を生成したバージョンのオブジェクトモデルができました。デシリアライズでは、この事実に対応するために特別な測定を行うことができます。たとえば、Field1-Valueを他のオブジェクトのリストに書き込みます。

別のアプローチは次のようになります。

バージョン化されたシリアル化と逆シリアル化前の変換
は、上記のようにオブジェクトの状態をシリアル化します(バージョン属性を使用)。
シリアル化を解除するときに、バージョン属性を確認します。これが期待するバージョンでない場合は、xsl-scriptsまたはc#コードを使用してシリアル化されたオブジェクトの状態を現在のバージョンに変換します。現在のプロジェクトにxsl変換のリストを保存できます

- conversions
    - v1-v2
    - v2-v3

現在バージョン3を使用していて、xmlファイルをロードする場合は、バージョン属性を確認し、すべてのxslスクリプトを実行して、現在のバージョン(バージョン3)を取得します。したがって、xsl-script v1-v2を実行し、その後v2-v3を実行します。

この場合、逆方向の機能を気にする必要のない通常のシリアル化クラスと逆シリアル化クラスを使用できます。

于 2010-12-25T10:10:58.807 に答える
1

はい、これには必ず XML シリアライゼーションを使用する必要があります。しかし、Marc Gravell が指摘したように、最初に GUI コンポーネントによって表示されるデータを保持するオブジェクトが必要です。次に、最小限のコード行で実質的に自動 (逆) シリアル化を行うことができます。

于 2008-12-07T20:34:43.503 に答える
0

オブジェクト モデルを UI に結び付けるためにデータ バインディングを使用するのは非常に簡単です。

永続ストレージなしでオブジェクトを GUI コントロールに関連付けるにはどうすればよいですか? 手動で行う場合、メモリ内のすべてのオブジェクトに対して途方もない量のコードを書かなければならないことを意味します。私はすでにこのデータ用のある種のクラス ストレージを持っていますが、それはバインド ソート シナリオではありません。

シリアル化された XML をロードしてオブジェクトを取得し、オブジェクトを読み取って GUI 全体を埋めるローダーを作成する必要がありますか? 明らかに、これは拘束力のない手動ロードに似ています。何か不足していますか?

于 2008-12-07T21:20:43.380 に答える
0

これは、クラスまたは構造体をシリアライズ可能にする方法に関する素晴らしい記事です。必要なすべてのデータを保存できるクラスを作成します。クラスを直列化可能にします。このように、わずか数行のコードで、すべてのデータをファイルに保存できます。その後、数行のコードを追加するだけで、ファイルからデータを取得できます。

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

于 2008-12-07T21:27:39.967 に答える
0

クラスをシリアル化する代わりに、XML ファイルに永続化するための機能が組み込まれているデータ ストレージ用の ADO.NET データセットを使用することもできます。コードは最小限になり、実行中の操作のモデルに適合するテーブルを設計することで、必要な関連データのみを格納できます。さらに、後で UI 状態をローカル ファイルではなくデータベースに永続化する場合は、同じコードを使用できます。データセットを保存するための代替機能のみが必要です。

于 2008-12-07T23:59:51.857 に答える