You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对包含WinForms控件的Game与Store类进行(反)序列化?

解决WinForms中混合业务数据与控件的对象序列化问题

核心思路:分离业务数据与UI控件

WinForms控件(如CustomProgressBar、Button、Timer)包含大量与窗体上下文绑定的状态(句柄、父控件引用等),这些内容无法被正确序列化/反序列化。解决的关键是把业务数据和UI控件彻底拆分,只序列化纯数据对象。


1. 抽离纯业务数据类

创建独立的StoreData类,只存放可序列化的业务字段(字符串、数字、布尔值等),不包含任何控件引用:

[Serializable]
public class StoreData
{
    public string StoreName { get; set; }
    public int CurrentProgress { get; set; }
    public bool IsAvailable { get; set; }
    public DateTime LastUpdated { get; set; }
    // 其他业务相关字段...
}

2. 重构Store类:数据与控件绑定

原Store类保留控件引用,但通过StoreData来同步数据和控件状态,实现数据驱动UI:

public class Store
{
    // 业务数据容器(仅这个会被序列化)
    public StoreData Data { get; private set; }
    // UI控件(不参与序列化)
    public CustomProgressBar ProgressBar { get; set; }
    public Button ActionButton { get; set; }
    public Timer RefreshTimer { get; set; }

    // 从数据初始化Store(含控件状态同步)
    public Store(StoreData data)
    {
        Data = data;
        SyncDataToControls();
        BindControlEventsToData();
    }

    // 把业务数据同步到控件
    private void SyncDataToControls()
    {
        ProgressBar.Value = Data.CurrentProgress;
        ActionButton.Enabled = Data.IsAvailable;
        // 其他控件状态同步逻辑...
    }

    // 绑定控件事件,确保操作同步到业务数据
    private void BindControlEventsToData()
    {
        ProgressBar.ValueChanged += (s, e) => Data.CurrentProgress = ProgressBar.Value;
        ActionButton.Click += (s, e) => Data.IsAvailable = !Data.IsAvailable;
        // 其他控件事件绑定...
    }
}

3. 重构Game类:仅序列化数据列表

Game类不再存储Store对象,转而存储StoreData列表,序列化/反序列化只针对纯数据:

[Serializable]
public class Game
{
    public List<StoreData> StoreDatas { get; set; } = new List<StoreData>();

    // 从序列化的数据重建带控件的Store集合
    public List<Store> RebuildStores(Form parentForm)
    {
        var stores = new List<Store>();
        foreach (var data in StoreDatas)
        {
            var store = new Store(data)
            {
                // 动态创建控件(或关联设计器中已有的控件)
                ProgressBar = new CustomProgressBar()
                {
                    Size = new Size(200, 20),
                    Location = new Point(15, 20 + stores.Count * 35)
                },
                ActionButton = new Button()
                {
                    Text = Data.IsAvailable ? "启用" : "禁用",
                    Size = new Size(70, 20),
                    Location = new Point(220, 20 + stores.Count * 35)
                }
            };
            // 将控件添加到窗体
            parentForm.Controls.Add(store.ProgressBar);
            parentForm.Controls.Add(store.ActionButton);
            stores.Add(store);
        }
        return stores;
    }
}

4. 实现序列化/反序列化逻辑

只针对StoreData列表进行序列化操作,示例用BinaryFormatter(也可替换为Json.NET/System.Text.Json):

// 保存游戏数据
public void SaveGame(Game game, string savePath)
{
    using (var fs = File.Create(savePath))
    {
        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        formatter.Serialize(fs, game.StoreDatas);
    }
}

// 加载游戏数据并重建Store
public Game LoadGame(string savePath)
{
    var game = new Game();
    using (var fs = File.OpenRead(savePath))
    {
        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        game.StoreDatas = (List<StoreData>)formatter.Deserialize(fs);
    }
    return game;
}

关键注意事项

  • 绝对不要直接序列化WinForms控件:控件的上下文状态(如Handle、Parent)无法被序列化,强行操作会引发异常或内存泄漏。
  • 保持数据与控件同步:通过事件绑定确保控件操作实时更新业务数据,反序列化后通过SyncDataToControls恢复控件状态。
  • 序列化器选择:如果不需要二进制格式,Json.NET或System.Text.Json更轻量,且无需标记[Serializable]属性。

内容的提问来源于stack exchange,提问作者memelordtimelord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:52:49