首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C# DevExpress GridView 異步編輯刷新難題:委托與 Invoke 跨線程 UI 更新全解析

     編輯:程式設計疑難解答

問題場景

DevExpress GridView 中選中行彈出編輯窗口,子窗口保存數據關閉後,父窗口(MDI 子窗體)的 GridView 未自動刷新顯示最新數據。

核心原因

Windows Forms 控件非線程安全,子窗口嘗試直接訪問父窗口控件屬性/方法會導致跨線程調用異常或 UI 不更新。MDI 結構下更需注意窗體層級的同步上下文。

標準解決方案:委托 + Invoke 模式

步驟一:父窗體定義刷新委托與方法

public partial class ParentForm : DevExpress.XtraEditors.XtraForm {
    // 1. 定義委托類型
    public delegate void RefreshGridDelegate();
    
    // 2. 實現刷新邏輯(含線程檢查)
    public void RefreshGridData() {
        if (this.gridView1.InvokeRequired) {
            // 跨線程調用:通過 Invoke 執行
            this.Invoke(new RefreshGridDelegate(RefreshGridData));
            return;
        }
        
        // UI 線程安全執行區
        try {
            // 重新綁定數據源
            this.gridControl1.DataSource = GetLatestData(); // 你的數據獲取方法
            this.gridView1.RefreshData();
        } catch (Exception ex) {
            // 日誌記錄
        }
    }
    
    // 3. 子窗體調用入口
    public void OnChildDataSaved() {
        RefreshGridData();
    }
}

步驟二:子窗體持有父窗體引用並回調

public partial class EditForm : DevExpress.XtraEditors.XtraForm {
    private ParentForm _parentForm;
    
    public EditForm(ParentForm parentForm) {
        InitializeComponent();
        _parentForm = parentForm;
    }
    
    private void btnSave_Click(object sender, EventArgs e) {
        // 保存業務邏輯...
        if (SaveData()) {
            // 關鍵:通知父窗體刷新
            _parentForm.OnChildDataSaved();
            this.Close();
        }
    }
}

步驟三:父窗體打開子窗體時傳遞自身引用

private void gridView1_DoubleClick(object sender, EventArgs e) {
    var row = gridView1.GetFocusedDataRow();
    if (row != null) {
        using (var editForm = new EditForm(this)) { // 傳遞 this
            editForm.LoadData(row);
            editForm.ShowDialog();
        }
    }
}

進階優化方案

方案一:事件驅動模式(解耦)

// 父窗體定義事件
public event Action DataSaved;

// 子窗體觸發事件
public event Action OnSaveSuccess;
private void btnSave_Click(...) {
    if (SaveData()) {
        OnSaveSuccess?.Invoke();
        this.Close();
    }
}

// 父窗體訂閱
var editForm = new EditForm();
editForm.OnSaveSuccess += () => RefreshGridData();
editForm.ShowDialog();

方案二:BindingSource 自動同步(數據綁定場景)

// 父窗體使用 BindingSource
BindingSource bs = new BindingSource();
bs.DataSource = GetDataList();
gridControl1.DataSource = bs;

// 子窗體修改同一數據源對象(引用傳遞)後
// 父窗體只需調用
bs.ResetBindings(false); // 通知 UI 更新

方案三:async/await 現代化模式(.NET 4.5+)

private async void btnEdit_Click(object sender, EventArgs e) {
    var editForm = new EditForm();
    editForm.LoadData(selectedRow);
    
    // 非阻塞等待關閉
    await Task.Run(() => {
        editForm.ShowDialog();
    });
    
    // UI 線程自動恢復,直接刷新
    RefreshGridData();
}

常見坑點排查

現象可能原因解決
Invoke 後仍不刷新DataSource 未重新賦值重新賦值或調用 ResetBindings
跨線程異常未檢查 InvokeRequired統一使用 Invoke 模式
MDI 子窗體找不到父窗體MdiParent 屬性未正確設置子窗體構造函數接收父窗體參數
數據已更新但界面舊值緩存/狀態管理問題調用 gridView1.RefreshData() 或 LayoutChanged()

最佳實踐清單

  1. ✅ 父窗體暴露 RefreshGridData() 公共方法,內部處理 InvokeRequired
  2. ✅ 子窗體通過構造函數或屬性接收父窗體引用
  3. ✅ 保存成功後調用父窗體刷新方法,再關閉自身
  4. ✅ 使用事件模式降低耦合度(推薦中大型項目)
  5. ✅ 考慮 BindingSource 簡化數據綁定同步
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved