Dispatcher.BeginInvoke(new...ProgressBarProcess.Value = 1 没有显示?

Posted

技术标签:

【中文标题】Dispatcher.BeginInvoke(new...ProgressBarProcess.Value = 1 没有显示?【英文标题】:Dispatcher.BeginInvoke(new...ProgressBarProcess.Value = 1 not showing? 【发布时间】:2021-12-23 08:03:00 【问题描述】:

这适用于 Windows 10,Windows 应用程序 WPF .NET 框架 4.7.2

我有来自旧 Windows 窗体项目(与 2017 社区相比)的代码,用于运行良好的 ProgressBar(Windows 窗体项目)。我现在有一个工作正常的 wpf windows 项目,但我想为两个按钮(处理和插入按钮)单击事件添加一个进度条,因为它们需要几秒钟才能完成,我想看到一些进展。该项目仅供我使用,它始终在 Visual Studio(社区 2019)中运行。我将旧代码复制到新项目中,但进度条没有显示或显示进度。代码在旧项目的 buttonclick 事件中与新项目一样,但没有运气......我可能从旧项目中遗漏了一些东西,但我看不到什么。我知道“无法从调用线程修改 UI”,但我认为我在 Task.Factory.StartNew 中使用了不同的线程。这里有很多关于 Dispatcher.BeginInvoke 的帖子/线程,但我找不到与我的相似的……其中一个说这种方法是错误的方法。我不做 MVVM 的东西。我不经常做 Windows 编程 C#,所以不要以为我知道我在做什么......哈哈。任何帮助显示进度条将不胜感激。关于如何“正确”做的指针也很感激。谢谢。这是其中一个按钮 (ProcessButton_Click) 的代码,它的进度条被命名为“ProgressBarProcess”:

 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;
        

        Task.Factory.StartNew(() => ProcessButtonFunction());
    

 private void ProcessButtonFunction()
        
            ProgressBarProcess.Minimum = 1;
            ProgressBarProcess.Maximum = 5;
            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Visibility = Visibility.Visible; ));

            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 1; ));
            fillExcelLists();
            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 2; ));
            convertTofloatList();
            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 3; ));
            splitDesignatorList();
            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 4; ));
            fillRecordsList();
            Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 5; ));

            insertButton.IsEnabled = true;
            clearButton.IsEnabled = true;


        



private void fillExcelLists()
    
        ExcelAccess ExcelAccessOBJ = new ExcelAccess();

        //get part numbers from excel column to GlobalData.excelPartNoList
        ExcelAccessOBJ.ExcelReadColumn(partNoComboBox.SelectedItem.ToString(), Convert.ToInt16(dataStartRowTextBox.Text), Convert.ToInt16(dataEndRowTextBox.Text), GlobalData.excelPartNoList);

        //get quantities from excel column to GlobalData.excelQuantityList
        ExcelAccessOBJ.ExcelReadColumn(quantityComboBox.SelectedItem.ToString(), Convert.ToInt16(dataStartRowTextBox.Text), Convert.ToInt16(dataEndRowTextBox.Text), GlobalData.excelQuantityList);
       
        //get designators from excel column to GlobalData.excelDesignatorList
        ExcelAccessOBJ.ExcelReadColumn(designatorsComboBox.SelectedItem.ToString(), Convert.ToInt16(dataStartRowTextBox.Text), Convert.ToInt16(dataEndRowTextBox.Text), GlobalData.excelDesignatorList);

    
 

 public class ExcelAccess

    public void ExcelReadColumn(string column, int rowStart, int rowEnd, List<String> saveData)
    
        //set up excel stuff
        Microsoft.Office.Interop.Excel.Application ExcelObj = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel.Workbook 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.Sheets sheets = theWorkbook.Worksheets;
        Microsoft.Office.Interop.Excel.Range data_range;

        Microsoft.Office.Interop.Excel.Worksheet sheet = (Microsoft.Office.Interop.Excel.Worksheet)sheets.get_Item(1);      //first sheet

        //Read column
        for (int i = rowStart; i <= rowEnd; i++)
        
            data_range = sheet.get_Range(column + i.ToString(), column + i.ToString());     //read part number from excel sheet

            //Convert cell value to string 
            string tempString = Convert.ToString(data_range.Cells.Value2);
            if (tempString != string.Empty)
            
                saveData.Add(tempString);
            
            
        

        theWorkbook.Close();
        ExcelObj.Quit();

    


【问题讨论】:

ProgressBarProcess.Minimum = 1 抛出InvalidOperationException 使用断点:没有例外,但它似乎挂在 ProgressBarProcess.Minimum = 1 和 ProgressBarProcess.Maximum = 5,所以我将它们注释掉,现在它挂在 fillExcelLists();它确实显示了进度条。 每个 UIElement 像 ProgressBar 应该只能从 UI 线程访问。因此,每次访问 ProgressBar 时都要使用 Dispatcher,就像在设置可见性时那样。然后进入你的fillExcelLists 方法,看看它到底挂在哪里。 @Rodo:fillExcelLists和其他方法是如何实现的? 您是否启用了调试器以中断所有异常?您应该转到“异常设置”窗口(Ctrl+Alt+E)并确保至少启用节点“公共语言运行时异常”下的所有异常。调试器静默停止没有任何意义。否则,您将不得不进入 ExcelAccessOBJ.ExcelReadColumn 方法。 【参考方案1】:

留在调度程序线程上并等待在后台线程上执行长时间运行的方法的任务:

private async 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;
    

    ProgressBarProcess.Visibility = Visibility.Visible;
    ProgressBarProcess.Minimum = 1;
    ProgressBarProcess.Maximum = 5;
    ProgressBarProcess.Value = 1;
    await Task.Run(() => fillExcelLists());
    ProgressBarProcess.Value = 2;
    await Task.Run(() => convertTofloatList());
    ProgressBarProcess.Value = 3;
    await Task.Run(() => splitDesignatorList());
    ProgressBarProcess.Value = 4;
    await Task.Run(() => fillRecordsList());
    ProgressBarProcess.Value = 5;

    insertButton.IsEnabled = true;
    clearButton.IsEnabled = true;

真的不需要调度员。

【讨论】:

我猜这会在 fillExcelLists() 访问 UI 元素时引入跨线程异常。您应该从这些元素中收集数据并将它们作为参数传递给 fillExcelLists()(以从方法中删除关键引用)。 这几乎可以工作。进度条显示,但不显示“进度”。最后,它在 insertButton 显示启用的同时变为绿色。不过,我一点也不例外。哦,是的,我忘了说我从 fillExcelLists() 中删除了所有 UI 访问。它是唯一一个访问 UI。 @Rodo:那你真的在任务的后台线程上执行了长时间运行的方法吗?或者他们有多快?如果您暂时将fillExcelLists() 和其他方法替换为对Thread.Sleep(2000) 的调用,您应该会看到ProgressBar 在移动。 @mm8:我用你的代码替换了对 fillExcelLists() 的调用(如我的回答所示),看看它是否会显示进度。它没有。所以我尝试了 Task.Factory.StartNew(() => ProcessButtonFunction());它显示了进展。所以我选择了Task.Factory.StartNew ...我更喜欢你的代码,因为它更简单,但我无法让它工作。我可能稍后再试,但我现在正忙于做其他事情。感谢您的帮助。【参考方案2】:

这就是我最终要做的和工作的。我要感谢大家的帮助。我遇到的主要问题似乎是我从不应该访问的地方访问 UI。我读到的 ProgressBarProcess.Minimum 和 ProgressBarProcess.Maximum 似乎也不起作用(Link),这可能是我错过的。再次感谢:

 private void ProcessButton_Click(object sender, RoutedEventArgs e)
    
        if (targetBOMTextBox.Text.Length == 11)
        
            GlobalData.TargetBOMpartNo = targetBOMTextBox.Text;

            GlobalData.partNoColumn = partNoComboBox.SelectedItem.ToString();
            GlobalData.quantityColumn = quantityComboBox.SelectedItem.ToString();
            GlobalData.designatorColumn = designatorsComboBox.SelectedItem.ToString();

            GlobalData.dataRowStart = Convert.ToInt16(dataStartRowTextBox.Text);
            GlobalData.dataRowEnd = Convert.ToInt16(dataEndRowTextBox.Text);

        
        else
        
            MessageBox.Show("Target BOM part number missing or incorrect format.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            return;
        

        ProcessButton.IsEnabled = false;

        Task.Factory.StartNew(() => ProcessButtonFunction());
      
    

 private void ProcessButtonFunction()
    
        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Visibility = Visibility.Visible; ));

        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 10; ));
        fillExcelLists();
        convertTofloatList();
        splitDesignatorList();
        fillRecordsList();
        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 100; ));

        //insertButton.IsEnabled = true;    //this generates an exception do it like below
        Dispatcher.BeginInvoke(new Action(delegate ()  insertButton.IsEnabled = true; ));

        //clearButton.IsEnabled = true;  //this generates an exception do it like below
        Dispatcher.BeginInvoke(new Action(delegate ()  clearButton.IsEnabled = true; ));

        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Visibility = Visibility.Hidden; ));

    

 private void fillExcelLists()
    
        ExcelAccess ExcelAccessOBJ = new ExcelAccess();

        //get part numbers from excel column to GlobalData.excelPartNoList
        ExcelAccessOBJ.ExcelReadColumn(GlobalData.partNoColumn, GlobalData.dataRowStart, GlobalData.dataRowEnd, GlobalData.excelPartNoList);
        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 30; ));
        
        //get quantities from excel column to GlobalData.excelQuantityList
        ExcelAccessOBJ.ExcelReadColumn(GlobalData.quantityColumn, GlobalData.dataRowStart, GlobalData.dataRowEnd, GlobalData.excelQuantityList);
        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 60; ));

        //get designators from excel column to GlobalData.excelDesignatorList
        ExcelAccessOBJ.ExcelReadColumn(GlobalData.designatorColumn, GlobalData.dataRowStart, GlobalData.dataRowEnd, GlobalData.excelDesignatorList);
        Dispatcher.BeginInvoke(new Action(delegate ()  ProgressBarProcess.Value = 90; ));

    

【讨论】:

以上是关于Dispatcher.BeginInvoke(new...ProgressBarProcess.Value = 1 没有显示?的主要内容,如果未能解决你的问题,请参考以下文章

将 async/await 与 Dispatcher.BeginInvoke() 一起使用

WPF Dispatcher.BeginInvoke子线程更新UI

Application.Current.Dispatcher.BeginInvoke(action) VS。 Application.Current.Dispatcher.Invoke(action)

Dispatcher.BeginInvoke(new...ProgressBarProcess.Value = 1 没有显示?

Dispatcher.BeginInvoke()方法使用不当导致UI界面卡死的原因分析

BeginInvoke 调用的同步