Страницы

Поиск по вопросам

воскресенье, 2 февраля 2020 г.

Как загрузить вторую форму асинхронно и запустить её при нажатии кнопки пользователем?

#c_sharp #winforms #многопоточность


Дано: 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();
}

    


Ответы

Ответ 1



Всю долгую логику надо засунуть в конструктор второй формы (это я думаю логично). А дальше при запуске первой, в отдельном потоке создать вторую форму. И оставить её в памяти. По клику по кнопке её просто показать. Что то вот в этом роде: 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, или использовать таски и проверять при нажатии кнопки что таска завершилась).

Ответ 2



Не блокируйте поток UI долгими операциями. Выполняйте их асинхронно. Поэтому не стоит делать вещи вроде Thread.Sleep(). Не в коем случае не стоит в потоке UI вызывать вызывать методы (допустим SomeMethod(…)) возвращающие Task, просто SomeMethod(…).Result – это вызывает deadlock. Допустим есть метод возвращающий JSON: public static async Task 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; }

Комментариев нет:

Отправить комментарий