Progressbar на Waitform зависает, используя задачу, запущенную в другом потоке - PullRequest
1 голос
/ 07 мая 2019

У меня есть приложение, которое получает данные из таблиц Excel с помощью OLEDB.
В форме у меня есть элементы управления, чтобы пользователь мог фильтровать данные в соответствии со своими потребностями.
Например, FileSize, UserID,Rootpath и т. Д. Это прекрасно работает.

После окончательного выбора пользователь должен нажать кнопку «обновить», чтобы я мог отфильтровать данные на основе его ввода.Результат будет показан в Datagridview.

Однако, поскольку данные на листах Excel сильно различаются, у меня была индикатор выполнения во второй форме (Waitform) или делали представление данных невидимым, в то время как индикатор выполнения в пользовательском интерфейсевидимый во время не-UI-Задачи (Сбор данных).
Я знаю, что должен использовать Задачу или Поток (или BackGroundWorker), чтобы пользовательский интерфейс реагировал.
При этом он по-прежнему зависаетвсе приложение.

//Update Button which uses all the userdefined filters
private async void updateButton_Click(object sender, EventArgs e)
{
    WaitBarDatagrid.Visible = true; //Progressbar is called WaitBarDatagrid
    WaitBarDatagrid.Style = ProgressBarStyle.Marquee;
    WaitBarDatagrid.MarqueeAnimationSpeed = 30;

    dataGridView1.Visible = false;
    await Task.Run(() => QueryToExcel());
    dataGridView1.DataSource = FileInfos;
    WaitBarDatagrid.Visible = false;
    dataGridView1.Visible = true;
}

private void QueryToExcel()
{
    this.Invoke((MethodInvoker)delegate ()
    {
        string fSize;
        if (FileSizeComboBox.Text == "All Data")
        { fSize = "0"; }
        else if (FileSizeComboBox.Text == "> 1 MB")
        { fSize = "1000"; } // 1MB = 1000kB 
        else if (FileSizeComboBox.Text == "> 10 MB")
        { fSize = "10000"; } // 10MB = 10.000kB
        else if (FileSizeComboBox.Text == "> 100 MB")
        { fSize = "100000"; } // 100MB = 100.000kB 
        else if (FileSizeComboBox.Text == "> 1 GB")
        { fSize = "1000000"; } // 1 GB = 1000.000 kB
        else
            fSize = "0";

        // The following ensures that all possibilities of User Definition are covered
        string user = "";
        string size = "";
        string sep = ""; //Seperator

        if (!string.IsNullOrEmpty(UserTextbox.Text))
        {
            user = $"[UserID] = '{UserTextbox.Text}'";
            sep = "AND";
        }

        if (!string.IsNullOrEmpty(FileSizeComboBox.Text))
        {
            size = $"{sep} [File Size] >= {fSize}";
            sep = "AND";
        }                    

        //Final Where CLAUSE based on User Input
        //string command = $@"{user} {size}{sep} [Date] <= {DateBox.Value.ToOADate()}";
        string command = $@"{user} {size} {sep} [Date] <= {DateBox.Value.ToOADate()}";

        //Call Data from Excel
        string connectionString = GetConnectionString(Datapath + RootCombobox.Text);
        string query = $@"SELECT * from [FileInfos$]  WHERE ({command})";
        DataTable dt = new DataTable();

        using (OleDbConnection conn = new OleDbConnection(connectionString))
        {
            conn.Open();
            using (OleDbDataAdapter dataAdapter = new OleDbDataAdapter(query, conn))
            {
                try
                {
                    dataAdapter.Fill(dt);
                    FileInfos = dt;
                }
                catch (System.Data.OleDb.OleDbException ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }                    
        }
    });
}

До сих пор я также пытался присвоить значения пользовательских входов глобальным переменным, и они будут изменены в их соответствующих событиях.Однако даже при вызове мой пользовательский интерфейс зависает.Откуда это?

Ответы [ 2 ]

2 голосов
/ 07 мая 2019

Предполагается, что метод QueryToExcel() ставит в очередь работу, выполняемую в пуле потоков, чтобы позволить потоку пользовательского интерфейса продолжать свою собственную работу без зависания.
Но вы заметили, что пользовательский интерфейс все равно зависает, говоря:

даже при вызове моего пользовательского интерфейса замораживается

Он вызывает поток пользовательского интерфейса из другого потока, который его замораживает.
Работа в другом потоке заключается в том, чтобы не использовать поток пользовательского интерфейса.Если мы возвращаем поток пользовательского интерфейса из рабочего потока, эффект теряется (или частично теряется, что раздражает в любом случае).

Вы также используете Invoke () вместо BeginInvoke () .Последний выполняется асинхронно: он немедленно возвращается и может предотвратить взаимные блокировки, если вызванный элемент управления занят, или иным образом недоступен / не отвечает.
В любом случае, это не предотвратит заикание пользовательского интерфейса.

Глядя на код, который вы здесь представили, кажется, что нет необходимости вообще вызывать поток пользовательского интерфейса: вторичному потоку просто нужны значения свойств некоторых элементов управления, а затем назначается DataTable для поля.

Затем можно передать этому методу требуемые значения в качестве аргументов, присваивая свойства Controls некоторым переменным или свойствам класса (чтобы было легче понять, что содержат аргументы).

рабочий метод может быть изменен в

private DataTable QueryToExcel(string[] paramArray) 
Or
private DataTable QueryToExcel(SomeClass values) 

и может вызываться как:

private async void updateButton_Click(object sender, EventArgs e)
{
    var dt = await Task.Run(() => QueryToExcel(values));
    Or
    dataGridView1.DataSource = await Task.Run(() => QueryToExcel(values));
}

В QueryToExcel() в Excel:

  • Доступ к параметру values для настройки запроса или другой обработки.
  • Создайте соединение с БД и заполните DataTable / DataSet.
  • Утилизировать все созданные одноразовые объекты (Connection / DataAdapter и т. Д.)
  • Возвращать таблицу данных
1 голос
/ 07 мая 2019

Ваш код в основном переходит к потоку, не являющемуся пользовательским интерфейсом, а затем возвращается к пользовательскому интерфейсу - это почти как если бы вы никогда не покидали поток пользовательского интерфейса.

Что вам нужно сделать, так это выполнить всю работу вашего пользовательского интерфейсав потоке пользовательского интерфейса и только в другом потоке работает не-пользовательский интерфейс.

Попробуйте этот код:

// Define other methods and classes here
//Update Button which uses all the userdefined filters
private async void updateButton_Click(object sender, EventArgs e)
{
    WaitBarDatagrid.Visible = true; //Progressbar is called WaitBarDatagrid
                                    //    WaitBarDatagrid.Style = ProgressBarStyle.Marquee;
                                    //    WaitBarDatagrid.MarqueeAnimationSpeed = 30;

    dataGridView1.Visible = false;

    string fSize;
    if (FileSizeComboBox.Text == "All Data")
    { fSize = "0"; }
    else if (FileSizeComboBox.Text == "> 1 MB")
    { fSize = "1000"; } // 1MB = 1000kB 
    else if (FileSizeComboBox.Text == "> 10 MB")
    { fSize = "10000"; } // 10MB = 10.000kB
    else if (FileSizeComboBox.Text == "> 100 MB")
    { fSize = "100000"; } // 100MB = 100.000kB 
    else if (FileSizeComboBox.Text == "> 1 GB")
    { fSize = "1000000"; } // 1 GB = 1000.000 kB
    else
        fSize = "0";

    // The following ensures that all possibilities of User Definition are covered
    string user = "";
    string size = "";
    string sep = ""; //Seperator

    if (!string.IsNullOrEmpty(UserTextbox.Text))
    {
        user = $"[UserID] = '{UserTextbox.Text}'";
        sep = "AND";
    }

    if (!string.IsNullOrEmpty(FileSizeComboBox.Text))
    {
        size = $"{sep} [File Size] >= {fSize}";
        sep = "AND";
    }

    //Final Where CLAUSE based on User Input
    //string command = $@"{user} {size}{sep} [Date] <= {DateBox.Value.ToOADate()}";
    string command = $@"{user} {size} {sep} [Date] <= {DateBox.Value.ToOADate()}";

    await Task.Run(() => QueryToExcel(command, RootCombobox.Text));
    dataGridView1.DataSource = FileInfos;
    WaitBarDatagrid.Visible = false;
    dataGridView1.Visible = true;
}

private void QueryToExcel(string command, string RootCombobox_Text)
{
    //Call Data from Excel
    string connectionString = GetConnectionString(Datapath + RootCombobox_Text);
    string query = $@"SELECT * from [FileInfos$]  WHERE ({command})";
    DataTable dt = new DataTable();

    using (OleDbConnection conn = new OleDbConnection(connectionString))
    {
        conn.Open();
        using (OleDbDataAdapter dataAdapter = new OleDbDataAdapter(query, conn))
        {
            try
            {
                dataAdapter.Fill(dt);
                this.Invoke((MethodInvoker)delegate () { FileInfos = dt; });
            }
            catch (System.Data.OleDb.OleDbException ex)
            {
                this.Invoke((MethodInvoker)delegate () { MessageBox.Show(ex.ToString()); });
            }
        }
    }
}

Он не проверен, но должен быть закрыт.Обратите внимание, что ни один из элементов пользовательского интерфейса не доступен и не обновляется в любом потоке, не являющемся пользовательским интерфейсом.

...