简体   繁体   English

WPF C# - 从另一个线程编辑列表框

[英]WPF C# - Editing a listbox from another thread

I realize what I'm doing is probably pretty silly, but I'm in the middle of learning WPF and would like to know how to do this. 我意识到我正在做的事情可能很愚蠢,但我正在学习WPF,并想知道如何做到这一点。

I have a window with a listbox on it. 我有一个带有列表框的窗口。 The listbox is being used to deliver status messages about the program while it's running. 列表框用于在程序运行时传递有关程序的状态消息。 For example "Server started" "New connection at IP #" etc. I wanted this to be constantly updating in the background, so I spawned a new thread for handling updating this, but when I made the call to add an item I get the error message "The calling thread cannot access this object because a different thread owns it." 例如“Server started”“IP#的新连接”等。我希望在后台不断更新,所以我产生了一个新的线程来处理更新这个,但是当我调用添加一个项目时,我得到了错误消息“调用线程无法访问此对象,因为另一个线程拥有它。”

Any idea how I can update the listbox from another thread? 知道如何从另一个线程更新列表框吗? Or in the background, etc. 或者在后台等


UPDATE UPDATE

If you are using C# 5 and .NET 4.5 or above you can avoid getting on another thread in the first place using async and await , eg: 如果您使用的是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; } 

Easy: 简单:

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

If you are in code-behind. 如果你在代码隐藏。 Otherwise you can access the Dispatcher (which is on every UIElement ) using: 否则,您可以使用以下命令访问Dispatcher(位于每个UIElement ):

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

Ok thats a lot in one line let me go over it: 好吧,在一行中有很多让我重复一遍:

When you want to update a UI control you, as the message says, have to do it from the UI thread. 当您想要更新UI控件时,正如消息所示,您必须从UI线程执行此操作。 There is built in way to pass a delegate (a method) to the UI thread: the Dispatcher . 内置了将委托(方法)传递给UI线程的方法: Dispatcher Once you have the Dispatcher you can either Invoke() of BeginInvoke() passing a delegate to be run on the UI thread. 拥有Dispatcher您可以Invoke() BeginInvoke() Invoke() BeginInvoke()传递一个委托,以便在UI线程上运行。 The only difference is Invoke() will only return once the delegate has been run (ie in your case the ListBox's new item has been added) whereas BeginInvoke() will return immediately so your other thread you are calling from can continue (the Dispatcher will run your delegate soon as it can which will probably be straight away anyway). 唯一的区别是Invoke()只会在委托运行后返回(即在你的情况下已经添加了ListBox的新项),而BeginInvoke()将立即返回,因此你调用的其他线程可以继续(Dispatcher将尽快运行你的代表,无论如何都可以直接运行你的代表。

I passed an anonymous delegate above: 我通过了一位匿名代表:

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

The bit between the {} is the method block. {}之间的位是方法块。 This is called anonymous because only one is created and it doesnt have a name (usually you can do this using a lambda expression but in this case C# cannot resolve the BeginInvoke() method to call). 这称为匿名,因为只创建了一个并且它没有名称(通常可以使用lambda表达式执行此操作,但在这种情况下,C#无法解析要调用的BeginInvoke()方法)。 Or I could have instantiated a delegate: 或者我可以实例化一个委托:

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

Then passed that: 然后通过:

 Dispatcher.Invoke(myDelegate); 

I also used the Action class which is a built in delegate but you could have created your own - you can read up more about delegates on MSDN as this is going a bit off topic.. 我还使用了Action类,它是一个内置的委托,但你可以创建自己的 - 你可以在MSDN上阅读更多关于委托的内容,因为这有点偏离主题..

You can also use the Action delegate with anonymous methods to update the main thread from the worker thread. 您还可以使用具有匿名方法的Action委托来更新工作线程中的主线程。 For more information on the Action class you could look here : 有关Action类的更多信息,请查看此处:

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

If you would like to update the listbox from multiple points I suggest explicitally setting the delegate, however if you just wish to update the thread at a single point in a method with a single call it could be done as follows : 如果您想从多个点更新列表框,我建议明确设置委托,但是如果您只是希望通过单个调用在方法中的单个点更新线程,则可以按如下方式完成:

        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.
        }));

Note that I use the Action class as it takes encapsulates a method that has a single parameter (in this case a listItem). 请注意,我使用Action类,因为它封装了一个具有单个参数的方法(在本例中为listItem)。

You'll want to use Dispatcher.BeginInvoke . 您将要使用Dispatcher.BeginInvoke For example, if the listbox is named listbox 例如,如果列表框名为listbox

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

This will queue up the delegate to be executed on the UI thread that listbox belongs to. 这将排队委托以在listbox所属的UI线程上执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM