C# - 从线程更新 windows 窗体元素
Posted
技术标签:
【中文标题】C# - 从线程更新 windows 窗体元素【英文标题】:C# - Updating windows forms elements from thread 【发布时间】:2019-06-29 03:41:59 【问题描述】:我目前正在创建一个程序来将笔记本电脑备份到 U 盘。我创建了一个从中调用方法的类。然后我通过一个单独的线程开始实际的备份。目前,我正在尝试通过此线程更改文本框和进度条。但是,它总是只立即显示第一个更改,而只有在程序运行后才显示其他进度。 我从互联网上尝试了几种解决方案,但到目前为止没有任何效果。也许有人在这里有解决方案。
backup sales_backup = new backup();
//Start Backup Button
private void backup_button_Click(object sender, EventArgs e)
Thread backupprocess = new Thread(new
ThreadStart(sales_backup.backup_start));
backupprocess.Start();
//Backup Function
public void backup_start()
files_to_copy = 0;
files_copied = 0;
var principalForm = System.Windows.Forms.Application.OpenForms.OfType<Form1>().FirstOrDefault();
if (principalForm.drive_selector.SelectedItem != null)
//Set Parameters
principalForm.backup_button.Visible = false;
error_copy = false;
error_message = "";
device_removed = false;
//Fill variables
string temp = principalForm.drive_selector.SelectedItem.ToString();
temp = regex_matching_return_match(temp, @"[A-Z,a-z](:\\)");
backup_device = temp;
//Set Backup device size
for (int i = 0; i < backup_devices_list.Count; i++)
if (backup_devices_list[i].backup_device_name == temp)
backup_device_size = backup_devices_list[i].device_size;
file_system = backup_devices_list[i].file_system;
double temp_free = calculate_GB(backup_devices_list[i].device_free_space.ToString());
device_free_space = temp_free;
break;
//If no device is initialized
if (backup_device == null || backup_device_size == 0)
write_to_textbox(get_create_usb_instance_error(), "red");
else //If select ist successfull
//Get Backup size
get_size();
if (backup_size < device_free_space)
backup_path_target = backup_device + "\\Backup\\";
Directory.CreateDirectory(backup_path_target);
//Get file count
get_file_count();
//Create Copy job
for (int i = 0; i < backup_path_source.Length; i++)
string backup_path_s = backup_path_source[i] + "\\";
string backup_path_t = backup_path_target + backup_path_target_folders[i] + "\\";
copy_function(backup_path_s, backup_path_t);
int progress = return_progress();
TextBox test = principalForm.textBox2;
ProgressBar progress_bar = principalForm.progressBar1;
//Delegate Textbox
if (test.InvokeRequired)
test.Invoke(new Action(() => test.Text = "Copying: " + backup_path_t));
else
test.Text = "Copying: " + backup_path_t;
//Delegate Progressbar
if (progress_bar.InvokeRequired)
test.Invoke(new Action(() => progress_bar.Value = progress));
else
progress_bar.Value = progress;
【问题讨论】:
您无法从后台线程更新 UI。您也不需要需要Invoke
,如果您使用async/await
和Process<T>
类向其他线程报告进度
如果您使用Task.Run
和async/await
,则不必使用Invoke().
。如果 UI 线程很忙,则调用自身 blocks。您也应该将 UI 与文件处理代码分开 - 当您可以将驱动器等作为方法参数传递时,无需像这样访问主窗体
【参考方案1】:
确保您从正确的线程进行更新。 GUI 只能从它自己的线程更新。更多here。
【讨论】:
【参考方案2】:我无法对此进行测试,但它至少应该可以帮助您到达那里:
private void InvokeIfRequired<C>(C control, Action<C> action) where C : Control
if (control.InvokeRequired)
control.Invoke((Action)(() => action(control)));
else
action(control);
private void backup_button_Click(object sender, EventArgs e)
var principalForm = System.Windows.Forms.Application.OpenForms.OfType<Form1>().FirstOrDefault();
if (principalForm.drive_selector.SelectedItem != null)
principalForm.backup_button.Visible = false;
string temp = principalForm.drive_selector.SelectedItem.ToString();
TextBox test = principalForm.textBox2;
ProgressBar progress_bar = principalForm.progressBar1;
Action<string> updateTest = t => this.InvokeIfRequired<TextBox>(test, c => c.Text = t);
Action<int> updateProgress = v => this.InvokeIfRequired<ProgressBar>(progress_bar, c => c.Value = v);
Thread backupprocess = new Thread(new ThreadStart(() => sales_backup.backup_start(temp, updateTest, updateProgress)));
backupprocess.Start();
//Backup Function
public void backup_start(string temp, Action<string> updateTest, Action<int> updateProgress)
files_to_copy = 0;
files_copied = 0;
//Set Parameters
error_copy = false;
error_message = "";
device_removed = false;
//Fill variables
temp = regex_matching_return_match(temp, @"[A-Z,a-z](:\\)");
backup_device = temp;
//Set Backup device size
for (int i = 0; i < backup_devices_list.Count; i++)
if (backup_devices_list[i].backup_device_name == temp)
backup_device_size = backup_devices_list[i].device_size;
file_system = backup_devices_list[i].file_system;
double temp_free = calculate_GB(backup_devices_list[i].device_free_space.ToString());
device_free_space = temp_free;
break;
//If no device is initialized
if (backup_device == null || backup_device_size == 0)
write_to_textbox(get_create_usb_instance_error(), "red");
else //If select ist successfull
//Get Backup size
get_size();
if (backup_size < device_free_space)
backup_path_target = backup_device + "\\Backup\\";
Directory.CreateDirectory(backup_path_target);
//Get file count
get_file_count();
//Create Copy job
for (int i = 0; i < backup_path_source.Length; i++)
string backup_path_s = backup_path_source[i] + "\\";
string backup_path_t = backup_path_target + backup_path_target_folders[i] + "\\";
copy_function(backup_path_s, backup_path_t);
int progress = return_progress();
//Delegate Textbox
updateTest("Copying: " + backup_path_t);
//Delegate Progressbar
updateProgress(progress);
【讨论】:
【参考方案3】:您是否尝试过使用progress_bar.Invoke((MethodInvoker)(() => progress_bar.Value = progress));
【讨论】:
这不是答案。它应该被删除。请赚取 50 个代表点来发布 cmets。 更改委托类型不会更改调用的内容。代码可以是progress_bar.Invoke(() => progress_bar.Value = progress);
。以上是关于C# - 从线程更新 windows 窗体元素的主要内容,如果未能解决你的问题,请参考以下文章
如何从 c# Windows 窗体中文本框的文本内容更新 datagridview 的列
C# Windows 窗体 - 带有事件的不可见元素(单击和 MouseEnter/Leave)