C# 使用后台工作进程更新和追加文本框值

发布于 2024-09-11 07:47:27 字数 2052 浏览 5 评论 0原文

我已经将 ac# windows 窗体应用程序放在一起。这相当简单:\

input:

  • 文本字符串
  • 源文件夹路径
  • 目标文件夹路径
  • 整数计数

该应用程序在源文件夹中的文本文件中搜索输入的文本字符串;如果找到该字符串,则会将该文件和同名的图像文件复制到目标文件夹。然而,它会根据整数输入多次执行此操作。

所以我有一个按钮,在按钮单击事件中我调用

ProcessImages(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString());

的是:

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
        {         
            int ImageCounter = 0;
            int MaxImages = Convert.ToInt32(strNumImages);

            DirectoryInfo di = new DirectoryInfo(SourceFolder);

            foreach (FileInfo fi in di.GetFiles("*.txt"))
            {
                if (fi.OpenText().ReadToEnd().Contains(DID))
                {
                    //found one!
                    FileInfo fi2 = new FileInfo(fi.FullName.Replace(".txt", ".tif"));
                    if (fi2.Exists)
                    {
                        try
                        {
                            tbOutput.Text += "Copying " + fi2.FullName + " to " + tbDest.Text + "\r\n";
                            fi2.CopyTo(tbDest.Text + @"\" + fi2.Name, true);
                            tbOutput.Text += "Copying " + fi.FullName + " to " + tbDest.Text + "\r\n";
                            fi.CopyTo(tbDest.Text + @"\" + fi.Name, true);

                            ImageCounter++;
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    }
                }

                if (ImageCounter >= MaxImages)
                    break;

            }

        }

发生的情况是进程运行良好,但我想在复制文件时更新表单上的文本框。基本上,表单在运行时会显示为空白,完成后输出将显示在文本框中。我想实现一个BackgroundWorker,让它在运行时更新UI。

我已经浏览了这些示例,但并没有真正遵循它们。我没有完成百分比值,我只想更新 .Text 更改每次迭代并显示它。我什至认为我不一定需要将实际的复制操作放在不同的线程中,只是听起来需要与主 UI 线程分开运行。也许我把这件事搞得太复杂了……有人能把我推向正确的方向吗?谢谢!

I've got a c# windows form app I threw together. It's fairly simple:\

inputs:

  • text string
  • source folder path
  • destination folder path
  • integer count

The app searches through text files in the source folder for the entered text string; if it finds the string then it copies that file and an image file with the same name to the destination folder. It does this however many times based on the integer input.

So I have a button, and in the button click event I call

ProcessImages(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString());

which is:

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
        {         
            int ImageCounter = 0;
            int MaxImages = Convert.ToInt32(strNumImages);

            DirectoryInfo di = new DirectoryInfo(SourceFolder);

            foreach (FileInfo fi in di.GetFiles("*.txt"))
            {
                if (fi.OpenText().ReadToEnd().Contains(DID))
                {
                    //found one!
                    FileInfo fi2 = new FileInfo(fi.FullName.Replace(".txt", ".tif"));
                    if (fi2.Exists)
                    {
                        try
                        {
                            tbOutput.Text += "Copying " + fi2.FullName + " to " + tbDest.Text + "\r\n";
                            fi2.CopyTo(tbDest.Text + @"\" + fi2.Name, true);
                            tbOutput.Text += "Copying " + fi.FullName + " to " + tbDest.Text + "\r\n";
                            fi.CopyTo(tbDest.Text + @"\" + fi.Name, true);

                            ImageCounter++;
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    }
                }

                if (ImageCounter >= MaxImages)
                    break;

            }

        }

What happens is that the process runs fine, but I want to update a textbox on the form with progress as the files are copied. Basically the form blanks out while it's running, and after it's finished the output is in the textbox. I'd like to implement a BackgroundWorker get it to update the UI while it's running.

I've looked through the examples but am not really following them. I don't have a percentage complete value, I just want to update .Text changes each iteration and have it display. I don't even think I necessarily need to put the actual copying action in different threads, it just sounds like that needs to be run separately from the main UI thread. Maybe I'm over complicating this altogether...can someone push me in the right direction? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜警司 2024-09-18 07:47:27

您与后台工作人员的关系是正确的。这是我整理的一个示例,向您展示如何执行此操作。使用 Form1 创建一个新的 Windows 应用程序。向其中添加 4 个控件:label1、backgroundWorker1、button1 和 button2。然后使用这个隐藏代码。然后您可以使用 ReportProgress userState 向主线程报告您想要的任何内容。在此示例中,我传递一个字符串。然后,ProgressChanged 事件处理程序位于 UI 线程上,并更新文本框。

    public partial class Form1 : Form
{
    int backgroundInt;
    public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState as string;
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundInt = 1;
        while (backgroundWorker1.CancellationPending == false)
        {
            System.Threading.Thread.Sleep(500);
            backgroundWorker1.ReportProgress(0, 
                String.Format("I found file # {0}!", backgroundInt));
            backgroundInt++;
        }
    }


    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        backgroundWorker1.CancelAsync();
    }
}

You are on the right track with the background worker. Here is an example I put together to show you how to do this. Create a new windows app with Form1. Add 4 controls to it: label1, backgroundWorker1, button1, and button2. Then use this code-behind. Then you can use the ReportProgress userState to report back to the main thread whatever you want. In this example, I am passing a string. The ProgressChanged event handler is then on the UI thread, and updates the textbox.

    public partial class Form1 : Form
{
    int backgroundInt;
    public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState as string;
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundInt = 1;
        while (backgroundWorker1.CancellationPending == false)
        {
            System.Threading.Thread.Sleep(500);
            backgroundWorker1.ReportProgress(0, 
                String.Format("I found file # {0}!", backgroundInt));
            backgroundInt++;
        }
    }


    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        backgroundWorker1.CancelAsync();
    }
}
情话难免假 2024-09-18 07:47:27

如果您使用后台工作程序,则可以使用 ReportProgress 方法返回任何整数,例如处理的记录数。它不一定是百分比。然后,在 ProgressChanged 处理程序中,您可以更新文本框。例如,

int count = e.ProgressPercentage;
textBox1.Text = string.Format("{0} images processed.", count);

如果您不想使用后台工作者,您可以在循环内调用Application.DoEvents()。这将使 UI 有机会刷新自身并响应用户操作。但要注意 - 它会大大减慢你的程序,所以你可能只想在每 100 次迭代时调用它。

If you use a background worker you can use the ReportProgress method to return any integer, such as the number of records processed. It doesn't have to be a percentage. Then, in the ProgressChanged handler you can update your textbox. E.g.

int count = e.ProgressPercentage;
textBox1.Text = string.Format("{0} images processed.", count);

If you don't want to use a background worker you can call Application.DoEvents() inside your loop. This will give the UI an opportunity to refresh itself and respond to user actions. But beware - it will slow your program a lot so you may want to call it only on every 100th iteration.

罪#恶を代价 2024-09-18 07:47:27

UI 不会更新,因为您不允许在长时间运行的文件处理循环中处理任何窗口消息。 WinForms 应用程序重绘以响应在主线程的消息队列中处理的 WM_PAINT 消息。

最简单的解决方案是强制更新 UI:在修改循环内的文本框后尝试在表单上调用 Update()。

您的应用程序仍然会被 UI 冻结(不响应鼠标点击等),但这至少应该让进度消息绘制在屏幕上。如果您真正需要的是更新显示,那么就到此为止。

下一个级别的解决方案是允许您的应用程序在文件处理循环中处理挂起的窗口消息。在循环中调用 Application.DoEvents() (而不是 form.Update)。这将允许表单根据文本输出更新重新绘制自身,并消除 UI 冻结 - 应用程序可以响应鼠标和键盘活动。

不过,这里要小心 - 用户可以在当前活动正在进行时单击启动当前活动的按钮 - 重入。您至少应该禁用启动长时间运行的文件处理的菜单或按钮,以防止重新进入。

第三级解决方案是使用后台线程进行文件处理。这引入了一系列您需要注意的新问题,并且在许多情况下线程是多余的。如果您不允许用户在文件处理发生时对您的应用程序执行任何其他操作,那么将文件处理推入后台线程就没有多大意义。

The UI doesn't update because you're not allowing any window messages to be processed in your long-running file processing loop. WinForms apps redraw in response to WM_PAINT messages which are processed in the message queue in the main thread.

The simplest solution is to force a UI update: Try calling Update() on your form after modifying the textbox inside your loop.

Your app will still be UI frozen (non responsive to mouse clicks, etc) but this should at least get the progress messages drawn on the screen. If updating the display is all you really need, then stop here.

The next level of solution would be to allow your application to process pending window messages in your file processing loop. Call Application.DoEvents() in your loop (Instead of form.Update). This will allow the form to redraw itself with your text output updates and will eliminate your UI freeze - the app can respond to mouse and keyboard activity.

Be careful here, though - the user could click the button that started the current activity while the current activity is in progress - reentrancy. You should at a minimum disable the menu or button that kicks off your long-running file processing to prevent reentrancy.

A third level of solution would be to use a background thread for the file processing. This introduces a whole host of new issues you need to be aware of, and in many cases threads are overkill. There's not much point in pushing the file processing off into a background thread if you're not going to allow the user to do anything else with your app while the file processing is happening.

染墨丶若流云 2024-09-18 07:47:27

只需创建一个 (1) 委托来包装您的 ProcessImages 方法调用,(2) 使用委托触发调用,以及 (3) 当您希望从 ProcessImages 方法更新文本框时,检查跨线操作并进行确保您从主线程进行更新:

delegate void ProcessImagesDelegate(string did, string sourceFolder, string destFolder, string strNumImages);

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
{
    // do work

    // update textbox in form
    if (this.textBox1.InvokeRequired)
    {
        this.textBox1.Invoke(new MethodInvoker(delegate() { this.textBox1.Text = "delegate update"; }));
    }
    else
    {
        this.textBox1.Text = "regular update";
    }

    // do some more work
}

public void MyMethod()
{
    new ProcessImagesDelegate(ProcessImages).BeginInvoke(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString(), null, null);
}

Just create a (1) a delegate to wrap your ProcessImages method call, (2) fire off the call using a delegate, and (3) when you wish to update the textbox from your ProcessImages method, check for cross-thead operation and make sure you do the update from the main thread:

delegate void ProcessImagesDelegate(string did, string sourceFolder, string destFolder, string strNumImages);

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages)
{
    // do work

    // update textbox in form
    if (this.textBox1.InvokeRequired)
    {
        this.textBox1.Invoke(new MethodInvoker(delegate() { this.textBox1.Text = "delegate update"; }));
    }
    else
    {
        this.textBox1.Text = "regular update";
    }

    // do some more work
}

public void MyMethod()
{
    new ProcessImagesDelegate(ProcessImages).BeginInvoke(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString(), null, null);
}
一笔一画续写前缘 2024-09-18 07:47:27

我不必创建后台工作者。我所做的只是调用 .Update() ,然后调用 System.Threading.Thread.Sleep(100) ,它就开始工作了。

I did not have to create a background worker. All I did was called .Update() followed by System.Threading.Thread.Sleep(100) and it started working.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文