从不同类中的不同线程更新 UI

发布于 2024-12-29 11:41:38 字数 625 浏览 2 评论 0原文

我有主窗体类,其中包含我想要更改的列表框。盒子里装满了通过耗时的方法创建的项目。现在它看起来像这样(手工发明一个示例,可能不是有效的 C#):

List<string> strings = StaticClassHelper.GetStrings(inputString);
foreach(string s in strings)
{
    listBox1.Add(s);
}

//meanwhile, in a different class, in a different file...

public static List<string> GetStrings(inputString)
{
    List<string> result = new List<string>();
    foreach(string s in inputString.Split('c'))
    {
        result.Add(s.Reverse());
        Thread.Sleep(1000);
    }
    return result;
}

我想做的是在发现新字符串时定期更新列表框。当线程方法位于同一个类中时,我发现其他答案有效,因此您可以设置事件处理程序。我在这里做什么?

I have the main form class which contains a list box I want to change. The box is populated with items created in a time-consuming method. Right now it looks like this (inventing an example by hand, might not be valid C#):

List<string> strings = StaticClassHelper.GetStrings(inputString);
foreach(string s in strings)
{
    listBox1.Add(s);
}

//meanwhile, in a different class, in a different file...

public static List<string> GetStrings(inputString)
{
    List<string> result = new List<string>();
    foreach(string s in inputString.Split('c'))
    {
        result.Add(s.Reverse());
        Thread.Sleep(1000);
    }
    return result;
}

What I would like to do instead is regularly update the list box as new strings are found. The other answers I found work when the thread method is in the same class, so you can set up an event handler. What do I do here?

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

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

发布评论

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

评论(3

心房敞 2025-01-05 11:41:38

我喜欢这样做,我在表单上创建一个方法,如下所示:

public void AddItemToList(string Item)
{
   if(InvokeRequired)
      Invoke(new Action<string>(AddItemToList), Item);
   else
      listBox1.Add(Item);
}

我更喜欢在这种情况下调用以确保同步添加项目,否则它们可能会乱序。如果您不关心顺序,那么您可以使用 BeginInvoke ,这会更快一些。由于此方法是公共的,因此只要您可以获得对表单的引用,您就可以从应用程序中的任何类中使用它。

这样做的另一个优点是,您可以从 UI 线程或非 UI 线程调用它,并且它负责决定是否需要 Invoke。这样你的调用者就不需要知道他们正在哪个线程上运行。

更新
为了解决您关于如何获取对 Form 的引用的评论,通常在 Windows 窗体应用程序中,您的 Program.cs 文件看起来像这样:

static class Program
{
   static void Main() 
   {
       MyForm form = new MyForm();
       Application.Run(form);  
   }

}

这通常是我会做的,特别是在以下情况下“单一表单”应用程序:

static class Program
{
   public static MyForm MainWindow;

   static void Main() 
   {
       mainWindow = new MyForm();
       Application.Run(form);  
   }

}

然后您几乎可以在任何地方通过以下方式访问它:

Program.MainWindow.AddToList(...);

Here is how I like to do this, I create a method on the form like this:

public void AddItemToList(string Item)
{
   if(InvokeRequired)
      Invoke(new Action<string>(AddItemToList), Item);
   else
      listBox1.Add(Item);
}

I prefer invoke in this case to make sure the items are added synchronously, otherwise they can get out of order. If you don't care about the order then you can use BeginInvoke which will be a tad faster. Since this method is public, you can all it from any class in your application as long as you can get a reference to your form.

Another advantage of this is that you can call it from either your UI thread or a non-UI thread and it takes care of deciding whether or not it needs Invokeing. This way your callers don't need to be aware of which thread they are running on.

UPDATE
To address your comment about how to get a reference to the Form, typically in a Windows Forms app your Program.cs file looks something like this:

static class Program
{
   static void Main() 
   {
       MyForm form = new MyForm();
       Application.Run(form);  
   }

}

This is typically what I would do, particularly in the case of a "Single Form" application:

static class Program
{
   public static MyForm MainWindow;

   static void Main() 
   {
       mainWindow = new MyForm();
       Application.Run(form);  
   }

}

And then you can access it pretty much anywhere with:

Program.MainWindow.AddToList(...);
生死何惧 2025-01-05 11:41:38

包含 ListBox 的类需要公开一个方法来添加字符串 - 由于该方法可能在不同的线程上调用,因此需要使用它

listBox1.Invoke( ...)

来创建线程安全的调用机制

The class containing the ListBox needs to expose a method to add a string - since this method might be called on a different thread, it needs to use

listBox1.Invoke( ...)

to create a thread-safe calling mechanism

向日葵 2025-01-05 11:41:38

您可以将 GetStrings 重写为迭代器吗?然后,在您的 UI 中,您可以启动一个后台线程来迭代 GetStrings 的结果,每次更新列表框。类似于:

public static System.Collections.IEnumerable GetStrings(inputString)
{
    foreach(string s in inputString.Split('c'))
    {
        yield return s.Reverse();
        Thread.Sleep(1000);
    }
}

在 UI 中(假设 C# 4):

Task.Factory.StartNew(() =>
{
    foreach (string s in StaticClassHelper.GetStrings(inputString))
    {
        string toAdd = s;
        listBox1.Invoke(new Action(() => listBox1.Add(toAdd)));
    }
}

可能是更简洁的方法,但这应该可以满足您的需求。

Would it be possible for you to rewrite GetStrings as an Iterator? Then in your UI you could start a background thread which iterates over the results of GetStrings, updating the listbox each time. Something like:

public static System.Collections.IEnumerable GetStrings(inputString)
{
    foreach(string s in inputString.Split('c'))
    {
        yield return s.Reverse();
        Thread.Sleep(1000);
    }
}

And in the UI (Assuming C# 4):

Task.Factory.StartNew(() =>
{
    foreach (string s in StaticClassHelper.GetStrings(inputString))
    {
        string toAdd = s;
        listBox1.Invoke(new Action(() => listBox1.Add(toAdd)));
    }
}

Probably cleaner ways to go about it, but this should get you what you're looking for.

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