【问题标题】:Updating progressbar external class更新进度条外部类
【发布时间】:2013-11-04 13:28:32
【问题描述】:

基本上,我有一个类,里面有一个函数,它计算文本文件中的所有行。

我需要它来更新 Form1 的进度条,每行计数。我试过了:

public static void rfile(string f)
{
    string[] lines = File.ReadAllLines(f);
    Form1 form = new Form1();
    foreach (string l in lines)
    {
        form.increaseProg();
    }
}

Form.cs

public void increaseProg()
{
    progressBar.Value++;
    System.Threading.Thread.Sleep(1000);
    progressBar.Refresh();
}

但这似乎并没有增加进度条。

【问题讨论】:

  • 使类引发表单订阅的自定义事件。否则,您需要在创建表单时将对表单的引用传递给类,以便更新屏幕上显示的实际表单。
  • 你能举个例子吗?我不太清楚你的意思。对不起。

标签: c#


【解决方案1】:

您可以利用Progress 类在长时间运行的操作中让所有相关人员都能轻松更新 UI。在您的表单中创建Progress 类,并指出当它获得进度时它应该如何更新UI。然后将该对象提供给将要进行长期工作的其他类:

private void button1_Click(object sender, EventArgs args)
{
    Progress<int> progress = new Progress<int>();
    progress.ProgressChanged += (p, value) => progressbar1.Value = value;
    Task.Run(() => SomeOtherClass.DoWork("c:/temp.txt", progress));
}

长时间运行的工作当然是在另一个线程中完成,以避免阻塞 UI。 Progress 类将负责为我们将 ProgressChanged 事件编组到 UI 线程,因此我们无需考虑。

现在对于工人,我们只需要在需要时报告进度:

public class SomeOtherClass
{
    public static void DoWork(string filepath, IProgress<int> progress)
    {
        int currentProgress = 0;
        foreach (var line in File.ReadLines(filepath))
        {
            DoSomethingWithLine();
            currentProgress++;
            progress.Report(currentProgress);
        }
    }
}

请注意,这种方法的另一个优点是SomeOtherClass 不需要了解有关表单的任何信息。任何可以提供IProgress 对象的人都可以调用它。如果您有其他形式需要调用该方法,则根本不需要更改它。这也意味着,如果一个开发人员正在编写表单,而另一个开发人员正在编写长期运行的流程,他们只需要就DoWork 方法的签名达成一致;从那时起,UI 人员可以完成所有 UI 工作,非 UI 人员可以完成所有非 UI 工作,他们无需担心其他人在做什么。

至于为什么您的代码不起作用,问题是您的工作方法没有访问正在显示的表单的实例,您正在创建一个全新的表单,修改它的进度条,从不显示给任何人,然后扔掉。

【讨论】:

    【解决方案2】:

    这是因为您正在阅读这些行,并且在更新进度条时,UI 不会像您想象的那样完全刷新。你需要做的,因为它有时可以工作,是做一个 progressBar.Refresh() (我不记得进度条是否有刷新功能),所以它可以在工作时更新表单视觉效果(特别是进度条)通过线条的方式。基本上,它“太快”会导致更新延迟。

    进度条正在更新,您只是看不到刷新。一种方法可能是使其成为多线程的,但这对于读取行和更新 UI 来说太过分了。

    不仅如此,您的代码基本上不显示 UI...它正在更新,并且不显示 UI,您需要 Show() 表单并对其执行更新。您实例化了表单,但实际上并没有显示它。

    【讨论】:

    • 我知道你来自哪里,但是当我打电话给班级时,它会调用public partial class Form1 : Form,然后我假设它会重新创建表单,因为public Form1() => InitializeComponent();。我这样说对吗?如果我是,还有什么方法可以解决它?
    • 不确定我是否理解。你创建了一个表单的实例——太好了。但是现在..您没有显示表格。如果你这样做:form.Show(),它将显示具有您放置在表单上的控件的表单,包括进度条
    • 我可以看到进度条和实际表格。不过我发现了一些东西。我在循环中添加了它,它在进度条上打开了一些带有 1 值的表单。所以它不知何故没有更新。所以在要更新的函数中,我添加了progressBar.Value = 100;,但它根本不会在表单本身上更新(值会更新)。我添加了.Refresh(),但仍然没有。就好像它在打开时被锁定到位。
    • 好的,就像我说的那样 - 这是因为它可能通过得太快而无法实际显示表单本身的任何更新。你确定你也在进度条上设置了最小值和最大值吗?如果您说,出于测试实践而不是出于生产原因,Thread.Sleep(1000) - 这将休眠 1 秒钟,然后您应该能够看到更新。在读取行时,在 for 循环中执行 Thread.Sleep(1000) 后,还要放置进度条 Refresh() 方法调用。
    • 我同意正在发生其他事情。我发布了 3 个都有效的解决方案,您的解决方案也有效。
    【解决方案3】:
    using System.Windows.Threading;
    Dispatcher.CurrentDispatcher.Invoke(new System.Action(() => ProgressBar1.Value = value));
    

    您有时可以在做其他工作的同时使用上面的代码来更新 UI。不过,您需要一些方法来以数字方式跟踪进度。

    如上所述,由于您将主线程与处理相关联,因此您无法在同一线程上更新 UI。上面的内容节省了一小部分时间来执行此操作。唯一的另一种方法是后台工作人员,尽管这对您的情况来说可能是多余的。

    编辑(在单独的表单上显示)
    您是否将进度条上的修饰符设置为公开?此解决方案可以很好地在单独的窗口中显示进度,并且可以正确更新。

        private void button1_Click(object sender, EventArgs e)
        {
            Form2 f2 = new Form2();
            f2.progressBar1.Maximum = 1000;
            f2.Show();
            for(int i = 0; i < 1000; i++)
            {
                Thread.Sleep(10);
                f2.progressBar1.Value++;
            }
        }
    

    请注意,进度窗口的 UI 将被锁定,直到底层进程完成。我刚刚对此进行了测试,它工作正常。

    更新同一表格的进度,但来自不同的班级
    您需要传递进度条的引用,以便您的方法知道它正在更新什么。

    }
        private void button1_Click(object sender, EventArgs e)
        {
            Class1 c = new Class1();
            progressBar1.Maximum = 1000;
            for(int i = 0; i < 1000; i++)
            {
                Thread.Sleep(10);
                c.IncreaseProgress(progressBar1);
            }
        }
    }
    
    class Class1
    {
        public void IncreaseProgress(ProgressBar p)
        {
            p.Value++;
        }
    }
    

    上述两种方法都会在更新时锁定 UI。使用 BackgroundWorker 来避免这种情况。不过,此解决方案在快速而肮脏的实用程序中运行良好。

    【讨论】:

    • 不。不工作。不过,请检查我对另一个答案的评论。会不会是这个问题?
    • 那么您的进度条是否出现在新表单上,而不是最初启动流程的表单上?
    • 没关系。我想我们现在可以排除这种可能性。我添加了一个检查,所以它不会第二次初始化它。即使刷新,它仍然没有更新。
    • 形式相同,但类不同。 increaseProg 在 Form1.cs 中,循环在 main.cs
    • 等等,你是从控制台应用程序调用 Winform 吗?那么为什么不直接在 Winforms 中编写整个内容呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-25
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 2020-11-05
    • 2014-04-09
    • 1970-01-01
    相关资源
    最近更新 更多