Как обновить графический интерфейс с помощью backgroundworker?

Делегатор конвертера помогает мне визуализировать обе совместно работающие концепции:

delegate TOutput Converter<in TInput, out TOutput>(TInput input);

TOutput представляет ковариацию, в которой метод возвращает более конкретный тип .

TInput представляет собой контравариантность, когда метод передается менее конкретным типом .

public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }

public static Poodle ConvertDogToPoodle(Dog dog)
{
    return new Poodle() { Name = dog.Name };
}

List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();
45
задан Mads Andersen 7 December 2009 в 22:00
поделиться

5 ответов

Вам нужно один раз объявить и настроить BackgroundWorker - затем вызвать метод RunWorkerAsync в вашем цикле ...

public class UpdateController
{
    private UserController _userController;
    private BackgroundWorker _backgroundWorker;

    public UpdateController(LoginController loginController, UserController userController)
    {
        _userController = userController;
        loginController.LoginEvent += Update;
        _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
        _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
        _backgroundWorker.WorkerReportsProgress= true;
    }

    public void Update()
    {
         _backgroundWorker.RunWorkerAsync();    
    }

    public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        while (true)
        {
        // Do the long-duration work here, and optionally
        // send the update back to the UI thread...
        int p = 0;// set your progress if appropriate
        object param = "something"; // use this to pass any additional parameter back to the UI
        _backgroundWorker.ReportProgress(p, param);
        }
    }

    // This event handler updates the UI
    private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // Update the UI here
//        _userController.UpdateUsersOnMap();
    }
}
46
ответ дан 26 November 2019 в 21:20
поделиться

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

Редактировать : Как другой пользователь опубликовал сообщение, если вы можете подождать, пока событие BackgroundWorker.Completed обновит ваш пользовательский интерфейс, вы можете подписаться на это событие и напрямую вызвать свой код пользовательского интерфейса. , BackgroundWorker_Completed вызывается в главном потоке приложения. мой код предполагает, что вы хотите делать обновления во время операции. Одной из альтернатив моего метода является подписка на событие BwackgroundWorker.ProgressChanged , но я считаю, что вам все равно придется вызывать Invoke для обновления вашего интерфейса в этом случае.

например

public class UpdateController
{
    private UserController _userController;        
    BackgroundWorker backgroundWorker = new BackgroundWorker();

    public UpdateController(LoginController loginController, UserController userController)
    {
        _userController = userController;
        loginController.LoginEvent += Update;
    }

    public void Update()
    {                        
         // The while loop was unecessary here
         backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
         backgroundWorker.RunWorkerAsync();                 
    }

    public delegate void DoUIWorkHandler();


    public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
       // You must check here if your are executing on a background thread.
       // UI operations are only allowed on the main application thread
       if (someControlOnMyForm.InvokeRequired)
       {
           // This is how you force your logic to be called on the main
           // application thread
           someControlOnMyForm.Invoke(new             
                      DoUIWorkHandler(_userController.UpdateUsersOnMap);
       }
       else
       {
           _userController.UpdateUsersOnMap()
       }
    }
}
11
ответ дан Mike Marshall 7 December 2009 в 22:00
поделиться

Вы можете использовать событие RunWorkerCompleted в классе backgroundWorker, чтобы определить, что следует делать после завершения фоновой задачи. Итак, вы должны выполнить вызов базы данных в обработчике DoWork, а затем обновить интерфейс в обработчике RunWorkerCompleted, примерно так:

BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (o, e) => { longRunningTask(); }

bgw.RunWorkerCompleted += (o, e) => {
    if(e.Error == null && !e.Cancelled)
    {
        _userController.UpdateUsersOnMap();
    }
}

bgw.RunWorkerAsync();
4
ответ дан 26 November 2019 в 21:20
поделиться

В дополнение к предыдущим комментариям взгляните на www.albahari.com/threading - лучший документ по многопоточности, который вы когда-либо найдете. Он научит вас, как правильно использовать BackgroundWorker.

Вам следует обновить графический интерфейс, когда BackgroundWorker запускает событие Completed (которое вызывается в потоке пользовательского интерфейса, чтобы упростить вам задачу, чтобы вам не приходилось выполнять Control.Invoke самостоятельно).

3
ответ дан 26 November 2019 в 21:20
поделиться

Отражение - это один из способов сделать this ( SetField ).

Второй способ вызвать это любопытство - это если вы слишком рано раздадите ссылку, передав this (или что-нибудь, включающее неявное this , например, поле, захваченное анонимным методом / лямбда) из .ctor :

public MyClass(int id)
{
    Program.Test(this); // oopsie ;-p
    HashPrefs = new Hashtable();
}

Или более вероятно (с учетом вопроса):

SomeQueue.Add(this);

Другой важный вопрос может быть - может это не должно быть присвоено в первую очередь? И ответ на это - да, особенно если вы используете сериализацию. Вопреки распространенному мнению, вы можете обойти конструкторы , если у вас есть причина;

5
ответ дан 26 November 2019 в 21:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: