如何对包含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
相关产品推荐
相关产品推荐

