Использование BackgroundWorker с ProgressBar в WPF

#c# #wpf #multithreading #wpf-controls #backgroundworker

#c# #wpf #многопоточность #wpf-элементы управления #backgroundworker

Вопрос:

Привет, у меня есть приложение, в котором одним из заданий будет преобразование Excel и передача всех записей в базу данных.

Итак, это занимает немного времени, потому что я получу и вставлю в базу данных более 7000 строк.

Итак, поскольку эта работа занимает немного времени, более 3 минут, я хотел бы использовать ProgressBar для отчета о ходе выполнения этой работы.

Итак, если я выполняю эту работу в классе, который я создал, как я могу использовать BackgroundWorker для сообщения о ходе выполнения на ProgressBar в моем случае?

Моя цель — быть точным в процентах от того, как продвигается прогресс, и как я могу использовать все это для отчета о прогрессе. Я никогда не работал с BackgroundWorkers.

Я думаю, и это только подсказка, может быть, хорошая или нет, что я сначала получаю количество строк в Excel, делаю это число некоторым Maxvalue в ProgressBar, а затем для каждой строки или через интервал сообщаю о прогрессе.

Это возможно? Как я могу это сделать?

Ответ №1:

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

По сути, вы создаете некоторый класс, который реализует INotifyPropertyChanged со свойством progress, к которому вы можете привязать свой ProgressBar. например

 public class Task : INotifyPropertyChanged
{
    private int _progress = 0;
    public int Progress
    {
        get { return _progress; }
        private set
        {
            if (_progress != value)
            {
                _progress = value;
                OnPropertyChanged("Progress");
            }
        }
    }

    public Task(ref ProgressChangedEventHandler progressChangedEvent)
    {
        progressChangedEvent  = (s, e) => Progress = e.ProgressPercentage;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  

Этот класс использует событие в конструкторе, которое обновит ход выполнения задачи, если оно возникнет, но вы можете обработать изменение хода выполнения любым удобным вам способом, например методом или путем создания свойства Progress public , так что вы можете просто изменить его произвольно.


Пример использования:

 <ProgressBar Minimum="0" Maximum="100" Height="20"
             Value="{Binding UpdateTask.Progress, Mode=OneWay}"/>
  
 // The event that should be raised when a progress occurs.
private event ProgressChangedEventHandler UpdateProgressChanged;

// The task the ProgressBar binds to.
private readonly Task _updateTask;
public Task UpdateTask
{
    get { return _updateTask; }
}

public MainWindow()
{
    // Instatiate task, hooking it up to the update event.
    _updateTask = new Task(ref UpdateProgressChanged);
}

private void OnUpdateProgressChanged(int progressPercentage)
{
    if (UpdateProgressChanged != null)
    {
        UpdateProgressChanged(this, new ProgressChangedEventArgs(progressPercentage, null));
    }
}

// Simple progress simulation
private void Button1_Click(object sender, RoutedEventArgs e)
{
    int progress = 0;
    DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(0.5) };
    timer.Tick  = (sSub,eSub) =>
    {
        progress  ;
        // Raise progress changed event which in turn will change
        // the progress of the task which in turn will cause
        // the binding to update which in turn causes the value
        // of the ProgressBar to change.
        OnUpdateProgressChanged(progress);
        if (progress == 100)
        {
            timer.Stop();
        }
    };
    timer.Start();
}
  

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

1. Хорошо, я, если хочу привязать прогресс того, что я здесь делаю, к progressbar, я использую свойство Progress, верно? Можете ли вы привести пример привязки progressbar к этому свойству?

2. Добавлен пример, также исправлена ошибка в классе task, коструктору необходимо получить событие по ссылке ( ref ключевое слово).

3. Возможно, ваша привязка нарушена , я протестировал это, и это действительно работает.

4. Хорошо, я создал новый проект приложения wPF и взял ваш пример здесь, и протестировал его, и чтобы узнать, обновляется ли значение в progBar, в конце метода OnUpdatedProgressChanged я использовал окно сообщения, чтобы показывать мне значение progressbar каждый раз, когда свойство progress увеличивается, и значение просто не меняется. Значение всегда равно 0, но свойство увеличивается…

5. Как я уже говорил ранее, работает ли привязка , есть ли какие-либо ошибки в окне вывода? Мой код не завершен , я выполняю привязку с использованием DataContext. Если вы не настолько знакомы с привязкой, прочитайте обзор .

Ответ №2:

Вот пример фонового рабочего с прогрессом.

Однако дважды проверьте, чтобы убедиться, что BGW будет работать в вашей ситуации. Если вы управляете Excel через COM-взаимодействие, для этого может потребоваться поток STA (а BGW — это поток MTA, а не STA).

В этом случае вам нужно будет использовать Task с STA scheduler или ваше собственное руководство Thread (я настоятельно рекомендую подход, основанный на Task STA scheduler).

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

1. Да, я использую Com-взаимодействие. Итак, подход к задаче, о котором вы говорите, — это тот, о котором упоминается в backgroudn worker со ссылкой progress, верно?

2. Да, за исключением того, что вам придется использовать StaTaskScheduler , на который я ссылался.