如何通过异步调用更新列表框
本文关键字:列表 更新 调用 何通过 异步 | 更新日期: 2023-09-27 18:29:51
我开发了一个windows窗体c#应用程序,我只想通过旋转另一个线程来更新主窗体中Listbox中的项目,而不阻塞GUI窗体。由于线程无法访问像listbox这样的表单实体,所以我想到了使用委托。下面的代码显示了我是如何使用委托来完成任务的,但它阻止了GUI表单。所以我只想将它转换为一个异步委托,它更新列表框而不阻止GUI表单
委托声明
delegate void monitoringServiceDel();
调用代理
new monitoringServiceDel(monitoringService).BeginInvoke(null, null);
委托方法实现
private void monitoringService()
{
this.listEvents.Invoke(new MethodInvoker(delegate()
{
int i = 0 ;
while (i<50)
{
listEvents.Items.Add("count :" + count++);
Thread.Sleep(1000);
i ++;
}
}));
}
对于Win Forms,您需要使用Control的Invoke方法:
在拥有控件的线程上执行指定的委托底层窗口句柄
基本场景是:
- 使用BackgroundWorker进行繁重的工作,以便在非UI阻塞线程上检索所有项目
- 在BackgroundWorker.RunWorkerCompleted事件中,使用控件的Invoke方法将项添加到控件(在您的情况下为ListBox)
大致如下:
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork;
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl;
bw.RunWorkerAsync();
这应该会让你朝着正确的方向前进。
编辑:工作样本
public List<string> MyList { get; set; }
private void button1_Click( object sender, EventArgs e )
{
MyList = new List<string>();
var bw = new BackgroundWorker();
bw.DoWork += ( o, args ) => MethodToDoWork();
bw.RunWorkerCompleted += ( o, args ) => MethodToUpdateControl();
bw.RunWorkerAsync();
}
private void MethodToDoWork()
{
for( int i = 0; i < 10; i++ )
{
MyList.Add( string.Format( "item {0}", i ) );
System.Threading.Thread.Sleep( 100 );
}
}
private void MethodToUpdateControl()
{
// since the BackgroundWorker is designed to use
// the form's UI thread on the RunWorkerCompleted
// event, you should just be able to add the items
// to the list box:
listBox1.Items.AddRange( MyList.ToArray() );
// the above should not block the UI, if it does
// due to some other code, then use the ListBox's
// Invoke method:
// listBox1.Invoke( new Action( () => listBox1.Items.AddRange( MyList.ToArray() ) ) );
}
如果您正在修改UI元素,那么您将不得不阻塞UI线程。如果项目是突发的,或者需要在添加每个项目之间进行处理,那么您可能需要考虑在幕后运行处理(通过后台工作人员或Task)。但是,如果您只是获取数据并填充列表,则需要使用UI线程。
最简单的解决方案是使用BackgroundWorker
控件和两个Panels
。这个想法是在加载表单时在前台有一个面板Visible
,并且在其中有一个ImageBox
,可以播放一个简单的加载gif。ListBox
将位于默认情况下不可见的另一个面板内,并且将位于第一个面板的正后方。
加载表单后,启动BackgroundWorker
并完成您必须执行的任何数据检索或更新。任务完成后,在ListBox中设置数据,只需打开ListBox
面板即可使其可见。
这样,您就可以半异步地加载ListBox
,而它不会在添加每个项目后更新。您可以随时使用此技术,而不仅仅是表单加载!
下面是一个代码示例:
namespace AsyncForm
{
public partial class Form1 : Form
{
private List<String> collectionItems = new List<String>();
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 20; i++)
{
((List<String>)e.Argument).Add("Something " + i);
System.Threading.Thread.Sleep(200);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listBox1.Items.AddRange(collectionItems.ToArray());
listBox1.Visible = true;
pictureBox1.Visible = false;
}
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(collectionItems);
}
}
}
您应该将更新UI和长时间进程的功能分开。
处理UI逻辑。。
private void UpdateUI(string item)
{
if (Thread.CurrentThread.IsBackground)
{
listEvents.Dispatcher.Invoke(new Action(() => //dispatch to UI Thread
{
listEvents.Items.Add(item);
}));
}
else
{
listEvents.Items.Add(item);
}
}
使用TaskParallel 进行异步处理
private void Dowork()
{
Task task = Task.Factory.StartNew(() =>
{
int i = 0;
while (i < 10)
{
Thread.Sleep(1000);
UpdateUI(i.ToString());
i++;
}
});
}