У меня возникли проблемы с моим решением 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));
}
}
}
}