Управление формой из BackgroundWorker - PullRequest
2 голосов
/ 03 сентября 2010

У меня есть MainWindow (form1) и класс с именем Module

В модуле есть метод для создания нового рабочего фона и изменения метки в MainWindow. Я попытался создать открытый или внутренний метод в коде MainWindow и вызвать его в классе Module, но, похоже, он не работает.

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

Извините, если я ничего не прояснил, если вам нужно что-то прояснить, дайте мне знать.

Module.cs

public class Module
{
    protected System.Diagnostics.PerformanceCounter cpuCounter;
    BackgroundWorker cpuUThread;
    private delegate void UIDelegate();
    MainWindow mn;

    public void runCPUUsage()
    {

        cpuUThread = new BackgroundWorker();
        cpuUThread.DoWork += new DoWorkEventHandler(cpuUThread_DoWork);
        cpuUThread.WorkerSupportsCancellation = true;
        cpuUThread.RunWorkerAsync();
        mn = new MainWindow();
    }

    void cpuUThread_DoWork(object sender, DoWorkEventArgs e)
    {
        cpuCounter = new System.Diagnostics.PerformanceCounter();
        cpuCounter.CategoryName = "Processor";
        cpuCounter.CounterName = "% Processor Time";
        cpuCounter.InstanceName = "_Total";

        try
        {
            mn.changeCPUULabel(getCurrentCpuUsage().ToString());
        }
        catch (Exception ex)
        {

        }
    }

    public double getCurrentCpuUsage()
    {
        return Math.Round(cpuCounter.NextValue(), 0);
    }

    public void disposeCpuUsage()
    {
        cpuUThread.CancelAsync();
        cpuUThread.Dispose();
    }
}

MainWindow - содержит метку (labelCPUU)

internal void changeCPUULabel(string val)
    {
        Dispatcher.Invoke(new UIDelegate(delegate
            {
                this.labelCPUU.Content = val;
            }));
    }
public double getCurrentCpuUsage()
    {
       return mod.getCurrentCpuUsage();
    }
void activateCPUU(){ mod.runCPUUsage(); }

Ответы [ 5 ]

6 голосов
/ 03 сентября 2010

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

// Instead of:
myMainForm.MyLabel.Text = "New Text";

// Write:
myMainForm.Invoke(new Action(() => { myMainForm.MyLabel.Text = "New Text"; }));
5 голосов
/ 03 сентября 2010

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

0 голосов
/ 03 сентября 2010

Это хорошая идея, чтобы не связывать форму тесно с модулем. Один из способов сделать это - определить интерфейс, который реализует форма, и заставить модуль принимать такой интерфейс в качестве параметра. Я приведу пример с некоторым кодом. Начнем с интерфейса:

public interface IDataReceiver
{
    void SetData(string data);
}

В моем примере я использую string для данных, но это может быть любой тип, который вам нужно использовать. Затем мы реализуем этот интерфейс в виде:

public partial class Form1 : Form, IDataReceiver
{


    private void Button_Click(object sender, EventArgs e)
    {
        // create a module intstance, passing this form as parameter
        var module = new SomeModule(this);
        module.DoSomeWork();
    }

    public void SetData(string data)
    {
        // use the data that is received
        txtSomeTextBox.Text = data;
    }

    // the rest of the form code left out to keep it short
}

Наконец модуль с BackgroundWorker:

public class SomeModule
{
    private BackgroundWorker _worker = new BackgroundWorker();
    private IDataReceiver _receiver;
    public SomeModule(IDataReceiver receiver)
    {
        _worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
        _worker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
        _worker.WorkerReportsProgress = true;
        _receiver = receiver;
    }

    void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        _receiver.SetData(e.UserState.ToString());
    }

    public void DoSomeWork()
    {
        // start the worker
        _worker.RunWorkerAsync();
    }

    private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // call method to pass data to receiver
        _receiver.SetData(e.Result.ToString());
    }

    private void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // do some work here
        // assign the resulting data to e.Result
        for (int i = 0; i < 10; i++)
        {
            _worker.ReportProgress(0, "some data " + i);
            Thread.Sleep(250);
        }
        e.Result = "Finished";
    }
}

Таким образом, модуль никоим образом не зависит от того, как выглядит ваш класс формы (он даже не подозревает, что он общается с формой). В моем примере я вызываю _receiver.SetData из обработчика событий RunWorkerCompleted, но это также можно сделать из обработчика событий ReportProgress.

Также обратите внимание, что форма является «движущей силой» здесь. Модуль не создает формы и не предпринимает никаких инициатив любого рода. Просто используется формой.

0 голосов
/ 03 сентября 2010

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

Еще одна идея, но не сильная сторона для меня, но у него возникла проблема на работес потоками GUI.Мне интересно, если ваш фоновый работник находится в другом потоке, чем GUI и это вызывает проблемы.GUI всегда должен быть в своем собственном потоке, код Sence в GUI может заблокировать GUI.Если вам нужно взаимодействовать между различными потоками, вам нужно найти вызов.

0 голосов
/ 03 сентября 2010

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

...