It's not the first time I come across delegates and I am as confused as I were the last time and the time before that. So once and for all I want to get the delgate-confusion cleared up.
My problem is as followed
Having a Graphical User Interface which only displays a ListView with some boud items, I want to load the data from a Data Connection which takes some time, to increase the comfort of using the application I have instancieted a BackgroundWorker and in the doWork-method I want to fetch the data and display it.
This is how I want it
Now this is Cross-Thread-Invoking and I wanted to solve this with Delegates which brings me here. Following this tutorial , I got a working Delegate, However it did not solve the problem, inside my delegate I cannot change my ListView, it still says it is on another thread.
I want to find an Easy explenation on delegates and how to use them to solve my problem. Also, should I think or design my software different?
Normally BackgroundWorker
communicates with the UI thread using ReportProgress
. You would hook up a delegate to receive those progress events before launching the background worker, and then the progress would be reported on the UI thread, where you're safe to change your ListView
.
The other alternative in Windows Forms is to call Control.Invoke
or Control.BeginInvoke
, passing in a delegate which will update the UI. That delegate will be executed on the UI thread. For an example of this, see my threading tutorial or Joe Albahari's .
The equivalent of this in WPF is the Dispatcher
- again, Invoke
and BeginInvoke
. You can access the dispatcher for a control with the Dispatcher
property.
You can't change a ui control from a different thread directly, you need to check the Control.InvokeRequired
property before you make a change.
See this example on msdn
Checkout this code, it does what you need:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnFill_Click(object sender, EventArgs e)
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync();
}
private delegate void AddItemToListViewDelegate(ListView view, ListViewItem item);
private void AddItemToListView(ListView view, ListViewItem item)
{
if (InvokeRequired)
{
Invoke(new AddItemToListViewDelegate(AddItemToListView), new object[] { view, item });
return;
}
view.Items.Add(item);
}
private delegate void ClearListViewItemsDelegate(ListView view);
private void ClearListView(ListView view)
{
if (InvokeRequired)
{
Invoke(new ClearListViewItemsDelegate(ClearListView), new object[] { view });
return;
}
view.Items.Clear();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
if (i == 0)
ClearListView(listView1);
var item = new ListViewItem();
item.Name = i.ToString();
item.Text = item.Name;
AddItemToListView(listView1, item);
}
}
}
And for WPF something similar is required. Note this is not working code. As I don't use WPF I can't vouch that this is solid code, but it should give you an idea. You may need to create an type derived from EventArgs to encapsulate your listview and listviewitems.
If I get time, I'll edit this post so that it works, but that will have to wait until this evening!
using System.Windows.Threading;
...
if (listView1.Dispatcher.Thread != Thread.CurrentThread)
{
listView1.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new EventHandler<ListViewAddEventArgs>(AddItemToListView), sender, new object[] { e } );
return;
}
listView1.Items.Add(e.File);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.