关闭模态编辑窗体后刷新父窗体FlowLayoutPanel中的数据
我有一个窗体,其中包含一个FlowLayoutPanel控件,该控件内加载了一系列用于展示产品的UserControl。每个UserControl都带有一个按钮,点击后会打开一个模态窗体,用于在数据库中编辑产品。目前我遇到的问题是:在数据库数据变更或关闭模态对话框后,不知道如何刷新FlowLayoutPanel中的数据。是否可以模拟DataGridView绑定数据源的行为,使得数据库中每次发生数据变更时,FlowLayoutPanel中的对应数据也能自动更新?
以下是我用于填充FlowLayoutPanel控件的代码:
var result = await _obavijest.GetAll<List<Model.Obavijest>>(); foreach (var x in result) { ucObavijest pp = new ucObavijest(); pp.lblNaslov.Text = x.Naslov; pp.lblID.Text = x.ObavijestId.ToString(); pp.lblDatumVrijeme.Text = x.VrijemeObjave.ToString(); flpObavijesti.Controls.Add(pp); }
我来帮你一步步解决这个问题,既能实现模态关闭后的手动刷新,也能模拟DataGridView那样的自动绑定效果:
1. 先封装通用的刷新方法
首先把填充FlowLayoutPanel的逻辑抽成独立方法,每次刷新前清空已有控件避免重复添加,这样不管是模态关闭还是数据库变更,都能直接调用它更新UI:
private async Task RefreshObavijestiFlowPanel() { // 清空已有控件,避免重复加载 flpObavijesti.Controls.Clear(); // 从数据库拉取最新数据 var result = await _obavijest.GetAll<List<Model.Obavijest>>(); foreach (var x in result) { ucObavijest pp = new ucObavijest(); pp.lblNaslov.Text = x.Naslov; pp.lblID.Text = x.ObavijestId.ToString(); pp.lblDatumVrijeme.Text = x.VrijemeObjave.ToString(); // 给编辑按钮绑定事件,打开模态窗体并处理关闭后的刷新 pp.btnEdit.Click += async (s, e) => { using (var editForm = new EditObavijestForm(x.ObavijestId)) { var dialogResult = editForm.ShowDialog(); // 若用户保存了修改,立即刷新面板 if (dialogResult == DialogResult.OK) { await RefreshObavijestiFlowPanel(); } } }; flpObavijesti.Controls.Add(pp); } }
然后在窗体加载时调用这个方法初始化数据:
private async void Form_Load(object sender, EventArgs e) { await RefreshObavijestiFlowPanel(); }
这样关闭编辑模态窗体并点击保存后,FlowLayoutPanel的内容就会自动刷新了。
2. 进阶:实现类似DataGridView的数据源绑定(自动更新)
如果想实现像DataGridView绑定数据源那样,数据变更时自动同步UI,可以用ObservableCollection<T>作为数据源,监听它的CollectionChanged事件,自动增删改对应的UserControl:
第一步:定义数据源并监听集合变化
在窗体类中声明ObservableCollection并绑定事件:
private ObservableCollection<Model.Obavijest> _obavijestiCollection; private void InitializeDataSource() { _obavijestiCollection = new ObservableCollection<Model.Obavijest>(); // 监听集合的增删改操作 _obavijestiCollection.CollectionChanged += ObavijestiCollection_CollectionChanged; }
实现集合变化的处理逻辑:
private void ObavijestiCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { switch (e.Action) { case System.Collections.Specialized.NotifyCollectionChangedAction.Add: // 添加新数据对应的UserControl foreach (Model.Obavijest newItem in e.NewItems) { AddObavijestToFlowPanel(newItem); } break; case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: // 删除数据对应的UserControl foreach (Model.Obavijest oldItem in e.OldItems) { var controlToRemove = flpObavijesti.Controls.OfType<ucObavijest>() .FirstOrDefault(uc => uc.lblID.Text == oldItem.ObavijestId.ToString()); if (controlToRemove != null) { flpObavijesti.Controls.Remove(controlToRemove); controlToRemove.Dispose(); // 释放控件资源 } } break; case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: // 替换数据:先移除旧控件,再添加新控件 foreach (Model.Obavijest oldItem in e.OldItems) { var controlToRemove = flpObavijesti.Controls.OfType<ucObavijest>() .FirstOrDefault(uc => uc.lblID.Text == oldItem.ObavijestId.ToString()); if (controlToRemove != null) { flpObavijesti.Controls.Remove(controlToRemove); controlToRemove.Dispose(); } } foreach (Model.Obavijest newItem in e.NewItems) { AddObavijestToFlowPanel(newItem); } break; case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: // 集合重置,清空所有控件 flpObavijesti.Controls.Clear(); break; } }
第二步:封装添加UserControl的方法
private void AddObavijestToFlowPanel(Model.Obavijest obavijest) { ucObavijest pp = new ucObavijest(); pp.lblNaslov.Text = obavijest.Naslov; pp.lblID.Text = obavijest.ObavijestId.ToString(); pp.lblDatumVrijeme.Text = obavijest.VrijemeObjave.ToString(); // 编辑按钮事件:更新数据后同步集合 pp.btnEdit.Click += async (s, e) => { using (var editForm = new EditObavijestForm(obavijest.ObavijestId)) { if (editForm.ShowDialog() == DialogResult.OK) { // 从数据库获取最新数据,更新集合中的项 var updatedItem = await _obavijest.GetById<Model.Obavijest>(obavijest.ObavijestId); var index = _obavijestiCollection.IndexOf(obavijest); if (index != -1) { _obavijestiCollection[index] = updatedItem; } } } }; flpObavijesti.Controls.Add(pp); }
第三步:初始化集合数据
在窗体加载时,把数据库数据加载到ObservableCollection中:
private async void Form_Load(object sender, EventArgs e) { InitializeDataSource(); var result = await _obavijest.GetAll<List<Model.Obavijest>>(); foreach (var item in result) { _obavijestiCollection.Add(item); } }
3. 数据库变更的自动感知
如果想实现其他地方修改数据库数据时,主窗体的FlowLayoutPanel也能自动刷新,可以通过以下方式实现:
- 用Entity Framework的话,可以监听
DbContext的SaveChanges事件,数据保存后触发刷新; - 自定义静态事件,在数据变更的地方触发事件,主窗体订阅后执行刷新。
比如自定义一个数据变更通知类:
public static class DataChangeNotifier { public static event Action ObavijestiChanged; public static void NotifyObavijestiChanged() { ObavijestiChanged?.Invoke(); } }
在编辑窗体的保存方法里触发事件:
// 保存数据后触发通知 await _obavijest.Update(updatedObavijest); DataChangeNotifier.NotifyObavijestiChanged();
然后在主窗体订阅事件:
private void Form_Load(object sender, EventArgs e) { DataChangeNotifier.ObavijestiChanged += DataChangeNotifier_ObavijestiChanged; // 其他初始化代码... } private async void DataChangeNotifier_ObavijestiChanged() { // 确保在UI线程执行刷新操作 if (InvokeRequired) { Invoke(new Action(async () => await RefreshObavijestiFlowPanel())); } else { await RefreshObavijestiFlowPanel(); } }
这样不管哪里修改了数据库中的Obavijest数据,主窗体的FlowLayoutPanel都会自动同步更新!
内容的提问来源于stack exchange,提问作者sasko

