Дано: 2 формы.
Главная форма содержит кнопку по которой вызывается второстепенная форма (форма 2).
Форма 2 содержит паузу System.Threading.Thread.Sleep(10000) для имитации сложной работы (загрузки компонентов, множества изображений, отрисовки).
Задача: мгновенно отобразить форму 2 при нажатии кнопки на главной форме. При этом до нажатия кнопки, пользователь не должен видеть форму 2 (даже мелькающую). Кнопку пользователь нажмет не ранее, чем через 10 секунд после старта программы.
Я перепробовал варианты с запуском в другом потоке, пытался использовать просто Task, BackgroundWorker, ThreadPool. Не получается, как правило проблема при вызове формы по второй кнопке.
Какие есть идея?
Один из примеров, как я пытался решить задачу:
private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, ea) =>
{
frm.Show();
// В форме 2 стоит this.Visible = false;
};
bw.RunWorkerAsync();
}
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("button1_Click");
this.Invoke(new MethodInvoker(frm.ShowForm));
}
// Form 2:
public void ShowForm()
{
Console.WriteLine("Form2 showForm");
this.Visible = true;
this.WindowState = FormWindowState.Normal;
this.BringToFront();
}
Всю долгую логику надо засунуть в конструктор второй формы (это я думаю логично). А дальше при запуске первой, в отдельном потоке создать вторую форму. И оставить её в памяти. По клику по кнопке её просто показать. Что то вот в этом роде:
public partial class Form1 : Form
{
private Form2 _form;
public Form1()
{
InitializeComponent();
new Thread(()=>{_form = new Form2();}).Start();
}
private void button1_Click(object sender, EventArgs e)
{
_form.Show();
}
}
Единственная проблема данного кода, если пользователь нажмет кнопку ДО того как форма загрузится, все упадет. Но тут можно или просто проверять на Null, или использовать таски и проверять при нажатии кнопки что таска завершилась).
Поэтому не стоит делать вещи вроде Thread.Sleep(). Не в коем случае не стоит в потоке UI вызывать вызывать методы (допустим SomeMethod(…)) возвращающие Task<T>, просто SomeMethod(…).Result – это вызывает deadlock.
Допустим есть метод возвращающий JSON:
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
Вот так делать неправильно, мы так получим deadlock.
private void Button1_Click(...)
{
var jsonTask = GetJsonAsync(...);
// Тут мы получаем deadlock
textBox1.Text = jsonTask.Result;
}
Вместо это правильно было бы сделать так:
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text = json;
}
Решаем задачи освобождения потока UI таким образом: добавляем модификатор async к обработчику события (это Load, Click и т.д., в основном они получаются async void). Далее внутри него выполняем какой-либо Task с помощью await.
Поэтому в итоге что-то получается вроде:
private async void Form_Load(object sender, EventArgs e)
{
await Task.Delay(10000); // Допустим сделаем ожидание 10 секунд
var r = await … // Некоторый Task<Result>;
}
Сборка персонального компьютера от Artline: умный выбор для современных пользователей