MVP с Background Worker (Исключение поднято)
У меня возникли проблемы с моим решением MVP, возможно, связанным с потоками. Я использую Compact Framework 3.5 и использую C#. Я могу использовать OpenNETCF, поэтому мне доступен BackgroundWorker.
У меня есть кусок кода (MyClient
), который подключается к веб-серверу с помощью сокетов. Код подключается к серверу и загружает данные (бесконечно, их поток), пока пользователь не остановит их. Поскольку загрузка данных бесконечна, ее нужно запускать в потоке, и я думаю, что здесь возникают проблемы. MyClient
объект имеет состояние, представленное как enum On
, Off
, Connecting
,Редактировать - просто чтобы уточнить, когда MyClient.Start() вызывается, он подключается к серверу. Затем он берет это соединение и сохраняет его для использования в потоке потоков для постоянной загрузки данных. Поэтому, когда вызывается Stop(), ему просто нужно получить флаг bool, чтобы сообщить потоку, используемому внутри MyClient, о Stop. Укороченный вариант ниже для наглядности.
public void Start()
{
//...
//Code to Connect to server...
stream = _connection.GetStream();
//...
//Code to send/receive data to confirm connection...
State = State.On;
//Start thread to read data constantly until stopped by user setting "_continueReadingData = false"
_continueReadingData = true;
Thread readData = new Thread(ReadData);
readData.IsBackground = true;
readData.Start();
//Note readData uses the stream variable saved above
}
Просмотр звонков с докладчиком _presenter.TurnOn();
, Ведущий вызывает модель с _model.Start();
, Идея состоит в том, что код MyClient запускается, сообщает об изменениях своего состояния и работает бесконечно в фоновом режиме до тех пор, пока пользователь не нажмет стоп. View
защищен вызовами Invoke/BeginInvoke для компонентов пользовательского интерфейса.
Я приложил пример кода моей модели ниже. Первоначально я использовал нормальный поток и получил его работать, как вы можете видеть ниже, он закомментирован. Здесь есть две проблемы: необходимость использовать Invoke для маршалинга обратно в поток пользовательского интерфейса для всего, что достигает представления, а также проблема в том, что любые возникающие исключения не возвращаются в поток пользовательского интерфейса, поэтому вместо этого они не могут быть обработаны и приводят к аварийному завершению. приложение. Это две проблемы, которые я пытаюсь решить.
С тех пор я попробовал BackgroundWorker (доступный в OpenNETCF, точно так же как обычный BackgroundWorker в.Net 2.0 и более поздних версиях), чтобы обрабатывать исключения и сортировку, как в коде ниже. Но с этим я не могу заставить его работать. Вместо этого, когда состояние изменяется и возвращается в GUI. Хотя Invoke называется, он жалуется InvalidOperationException - "Invoke or BeginInvoke cannot be called on a control until the window handle has been created"
, В некоторых исследованиях кажется, что поток создает собственный набор элементов управления. На данный момент я в замешательстве.
Может кто-нибудь протянуть руку, чтобы показать мне, как правильно запускать / завершать потоки в модели, чтобы они выполнялись в фоновом режиме, вызывать исключения обратно в обрабатываемую модель и маршалировать выполнение обратно в поток пользовательского интерфейса, чтобы у вас не было использовать Invoke на каждом элементе управления. Я уверен, что это должно быть возможно.
public class Model
{
public event EventHandler DataChanged;
public event EventHandler ErrorRaised;
private MyClient _client = new MyClient();
public Model()
{
//Register to events
_client.StateChanged += ClientStateChanged;
//Setup current values
State = _client.State;
}
void ClientStateChanged(NTRIPClient client, NTRIPState newState)
{
State = newState;
}
private State _state;
public State State
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
if (DataChanged != null)
{
DataChanged(this, EventArgs.Empty);
}
}
}
}
public void Start()
{
//Thread thread = new Thread(_NTRIPClient.Start);
//thread.IsBackground = true;
//thread.Start();
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.DoWork += _client.Start();
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
if (ErrorRaised != null)
{
ErrorRaised(this, new ErrorEventArgs(e.Error));
}
}
}
}
1 ответ
Проблема оказалась в том, что презентатор создавался в представлении, которое, в свою очередь, создавало модель. Эта модель была вызвана до того, как представление было полностью построено, и, таким образом, элементы управления еще не были созданы.
Большой вопрос из-за простой ошибки:)