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

WPF项目通过Dispatcher.BeginInvoke更新ProgressBar值不显示问题

问题根源

  • 你在非UI线程中直接修改ProgressBarProcess的Minimum、Maximum属性,以及最后直接修改insertButton、clearButton的IsEnabled属性,属于跨线程访问UI控件,会触发异常导致后续逻辑终止,这是进度条不工作的核心原因。
  • 后台线程执行的fillExcelLists方法中直接读取partNoComboBox、dataStartRowTextBox等UI控件的属性,同样属于跨线程访问,会触发异常。
  • WinForm对跨线程访问的校验默认比WPF宽松很多,所以旧代码能正常运行,迁移到WPF后就会失效。

修复代码

以下是最小改动即可生效的代码版本:

1. 修改Process按钮点击事件

private void ProcessButton_Click(object sender, RoutedEventArgs e)
{
    if (targetBOMTextBox.Text.Length != 11)
    {
        MessageBox.Show("Target BOM part number missing or incorrect format.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        return;
    }
    // 提前在UI线程读取所有需要的UI参数,不要传到后台线程读取
    var partNoCol = partNoComboBox.SelectedItem.ToString();
    var quantityCol = quantityComboBox.SelectedItem.ToString();
    var designatorCol = designatorsComboBox.SelectedItem.ToString();
    var startRow = Convert.ToInt16(dataStartRowTextBox.Text);
    var endRow = Convert.ToInt16(dataEndRowTextBox.Text);
    // 提前在UI线程设置进度条基础属性
    ProgressBarProcess.Minimum = 1;
    ProgressBarProcess.Maximum = 5;
    ProgressBarProcess.Visibility = Visibility.Visible;
    ProgressBarProcess.Value = 0;

    Task.Factory.StartNew(() => ProcessButtonFunction(partNoCol, quantityCol, designatorCol, startRow, endRow));
}

2. 修改ProcessButtonFunction方法

private void ProcessButtonFunction(string partNoCol, string quantityCol, string designatorCol, int startRow, int endRow)
{
    // 所有UI操作都走Dispatcher
    Dispatcher.Invoke(() => ProgressBarProcess.Value = 1);
    fillExcelLists(partNoCol, quantityCol, designatorCol, startRow, endRow);
    
    Dispatcher.Invoke(() => ProgressBarProcess.Value = 2);
    convertTofloatList();
    
    Dispatcher.Invoke(() => ProgressBarProcess.Value = 3);
    splitDesignatorList();
    
    Dispatcher.Invoke(() => ProgressBarProcess.Value = 4);
    fillRecordsList();
    
    Dispatcher.Invoke(() => 
    {
        ProgressBarProcess.Value = 5;
        insertButton.IsEnabled = true;
        clearButton.IsEnabled = true;
        // 执行完可自行添加隐藏进度条的逻辑
    });
}

3. 修改fillExcelLists方法

去掉方法内的UI控件访问逻辑,改用传入的参数:

private void fillExcelLists(string partNoCol, string quantityCol, string designatorCol, int startRow, int endRow)
{
    ExcelAccess ExcelAccessOBJ = new ExcelAccess();
    ExcelAccessOBJ.ExcelReadColumn(partNoCol, startRow, endRow, GlobalData.excelPartNoList);
    ExcelAccessOBJ.ExcelReadColumn(quantityCol, startRow, endRow, GlobalData.excelQuantityList);
    ExcelAccessOBJ.ExcelReadColumn(designatorCol, startRow, endRow, GlobalData.excelDesignatorList);
}

4. 可选优化:修复Excel进程残留问题

你当前的Excel操作代码如果中途抛异常,会导致Excel进程残留在任务管理器,占用文件锁,建议修改ExcelAccess类的实现:

public class ExcelAccess
{
    public void ExcelReadColumn(string column, int rowStart, int rowEnd, List<String> saveData)
    {
        Microsoft.Office.Interop.Excel.Application ExcelObj = null;
        Microsoft.Office.Interop.Excel.Workbook theWorkbook = null;
        try
        {
            ExcelObj = new Microsoft.Office.Interop.Excel.Application();
            theWorkbook = ExcelObj.Workbooks.Open(GlobalData.excelFilenameAndPath, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
            Microsoft.Office.Interop.Excel.Worksheet sheet = (Microsoft.Office.Interop.Excel.Worksheet)theWorkbook.Worksheets.get_Item(1);

            for (int i = rowStart; i <= rowEnd; i++)
            {
                var data_range = sheet.get_Range(column + i.ToString(), column + i.ToString());
                string tempString = Convert.ToString(data_range.Cells.Value2);
                if (!string.IsNullOrEmpty(tempString))
                {
                    saveData.Add(tempString);
                }
                // 即时释放COM对象
                System.Runtime.InteropServices.Marshal.ReleaseComObject(data_range);
            }
        }
        finally
        {
            if (theWorkbook != null)
            {
                theWorkbook.Close();
                System.Runtime.InteropServices.Marshal.ReleaseComObject(theWorkbook);
            }
            if (ExcelObj != null)
            {
                ExcelObj.Quit();
                System.Runtime.InteropServices.Marshal.ReleaseComObject(ExcelObj);
            }
        }
    }
}

实现优化建议

  • 所有UI控件的读写操作必须在UI线程执行,WPF对跨线程访问的校验没有宽松容错空间,不要在后台线程直接操作任何UI属性。
  • 后台线程执行逻辑前,提前把所有需要的UI参数读取出来传递到后台方法,避免后续重复写Dispatcher调用。
  • 简单场景下用Dispatcher更新UI即可,如果你后续需要更清晰的结构,可以用IProgress<T>接口实现进度通知,不用在业务代码里到处写Dispatcher调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 17:24:03