【问题标题】:WPF C# - Editing a listbox from another threadWPF C# - 从另一个线程编辑列表框
【发布时间】:2010-11-16 04:52:22
【问题描述】:

我意识到我正在做的事情可能很愚蠢,但我正在学习 WPF 并且想知道如何做到这一点。

我有一个窗口,上面有一个列表框。列表框用于在程序运行时传递有关程序的状态消息。例如“服务器已启动”“IP # 处的新连接”等。我希望它在后台不断更新,所以我产生了一个新线程来处理更新它,但是当我调用添加一个项目时,我得到了错误消息“调用线程无法访问此对象,因为不同的线程拥有它。”

知道如何从另一个线程更新列表框吗?或者在后台等。

【问题讨论】:

  • 我太困了,无法写出完整的答案,但这里有一个提示:环顾四周(Google、SO 或 MSDN)寻找 Dispatcher。现在,我要睡觉了。

标签: c# wpf multithreading


【解决方案1】:


更新

如果您使用的是 C# 5 和 .NET 4.5 或更高版本,则可以首先使用 asyncawait 避免进入另一个线程,例如:

private async Task<string> SimLongRunningProcessAsync()
{
    await Task.Delay(2000);
    return "Success";
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    button.Content = "Running...";
    var result = await SimLongRunningProcessAsync();
    button.Content = result;
}

简单:

Dispatcher.BeginInvoke(new Action(delegate() 
  {
     myListBox.Items.Add("new item"));
  }));

如果您在代码隐藏中。否则,您可以使用以下命令访问 Dispatcher(在每个 UIElement 上):

Application.Current.MainWindow.Dispatcher.BeginInvoke(...

好吧,一行就这么多,让我来复习一下:

当您想要更新 UI 控件时,正如消息中所说,您必须从 UI 线程中进行操作。有一种内置方法可以将委托(一种方法)传递给 UI 线程:Dispatcher。一旦你有了Dispatcher,你就可以通过Invoke()BeginInvoke() 传递一个要在UI 线程上运行的委托。唯一的区别是Invoke() 只会在委托运行后返回(即在您的情况下,ListBox 的新项目已添加)而BeginInvoke() 将立即返回,因此您调用的其他线程可以继续(调度程序将尽快运行您的委托,这可能会立即运行)。

我在上面传递了一个匿名委托:

delegate() {myListBox.Items.Add("new item");}

{} 之间的位是方法块。这被称为匿名,因为只创建了一个并且没有名称(通常您可以使用 lambda 表达式执行此操作,但在这种情况下,C# 无法解析要调用的 BeginInvoke() 方法)。或者我可以实例化一个委托:

Action myDelegate = new Action(UpdateListMethod);

void UpdateListMethod() 
{
  myListBox.Items.Add("new item");
}

然后通过了:

Dispatcher.Invoke(myDelegate);

我还使用了内置委托的 Action 类,但您可以创建自己的 - 您可以在 MSDN 上阅读有关委托的更多信息,因为这有点离题..

【讨论】:

  • 谢谢,非常感谢详细的解释!我是一名编程学生,但我学校的每个人都不喜欢 C#。我喜欢你更长的解释,因为它帮助我理解发生了什么。 C# 和 WPF 是一门相当庞大的语言,我只能从一本书中得到这么多。
  • 我很高兴(这与我给出的另一个答案相似)。如果是,请将其标记为答案:)
  • 我很喜欢,三年后回想起来,我真的知道调度程序、调用、委托和所有其他东西是什么(并且实际上知道如何使用它!):)
【解决方案2】:

您还可以使用带有匿名方法的 Action 委托从工作线程更新主线程。有关 Action 类的更多信息,您可以在这里查看:

http://msdn.microsoft.com/en-us/library/018hxwa8.aspx

如果您想从多个点更新列表框,我建议显式设置委托,但是如果您只想通过单个调用在方法中的单个点更新线程,可以按如下方式完成:

        listbox.Dispatcher.BeginInvoke(new Action(delegate()
        {
            listbox.Items.Add(item); //where item is the item to be added and listbox is the control being updated.
        }));

请注意,我使用 Action 类,因为它封装了一个具有单个参数(在本例中为 listItem)的方法。

【讨论】:

  • 我认为这样做。给出的另一个答案给了我一个编译器错误,说明它如何无法将兰巴演算转换为委托。我在 msdn 网站上做了一些阅读,并在 Google 上搜索了如何使用调度程序;我修改了他给我的代码,结果看起来和你那里的完全一样。 13 分钟前回答,我应该点击刷新,哈哈。
【解决方案3】:

您需要使用Dispatcher.BeginInvoke。例如,如果列表框名为listbox

  // On worker thread
  listbox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
  new Action(delegate() { listbox.Items.Add("Server started") });

这会将要在 listbox 所属的 UI 线程上执行的委托排队。

【讨论】:

  • 嘿,谢谢,您指出了正确的方向,但编译器不喜欢您在此处提供的内容。给出了一个关于它如何无法将lamba 演算转换为委托的错误。我能够对其进行更改以使其正常工作,不知道您的问题是什么,或者您只是使用我不理解的符号
  • 不,我只是懒得用实际程序检查自己。
猜你喜欢
  • 1970-01-01
  • 2017-05-16
  • 1970-01-01
  • 2020-10-15
  • 2010-10-18
  • 2011-08-12
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多