Dispatcher.CheckAccess () не работает из моего консольного приложения, есть ли лучший способ - PullRequest
0 голосов
/ 21 апреля 2010

Я написал приложение в WPF / VB и разделил бизнес-логику и пользовательский интерфейс на разные проекты.

Бизнес-уровень использует последовательный порт, который работает в другом потоке. Теперь, когда я пытаюсь написать интерфейс командной строки для того же бизнес-уровня, кажется, что происходит сбой при вызове .Invoke (). (без ошибок, просто не работает)

Я почти уверен, что причина, по которой мне пришлось добавить в checkaccess и .invoke, была в том, что у меня есть коллекции, которые будут изменены при обработке данных последовательного порта, и я хотел, чтобы NotifyCollectionChanged обрабатывался привязкой данных WPF. (Причина, по которой я не уверен на 100%, заключается в том, что несколько месяцев назад я написал эту часть, и все это прекрасно работало в графическом интерфейсе, а теперь добавление консольного приложения заставило меня переосмыслить часть этого)

Мне бы хотелось, чтобы мой бизнес-уровень запускал эти процессы в том потоке, в котором они были созданы. Мне нужно, чтобы он работал как с моей версией графического интерфейса, так и с версией командной строки.

Неправильно ли я использую Диспетчер на своем бизнес-уровне? Есть ли лучший способ обработать событие от последовательного порта, а затем вернуться в основной поток для обработки данных?

Изменено:

Private Delegate Sub NewDataRecieved(ByVal byteBuffer() As Byte)
Private Sub DataReceived(ByVal byteBuffer() As Byte) Handles _serial.DataRecieved
    If _dispatcher.CheckAccess() Then
        ProcessTheData
    Else
        Dim dataReceivedDelegate As NewDataRecieved
        dataReceivedDelegate = New NewDataRecieved(AddressOf DataReceived)
        _dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer)
    End If
End Sub

Ответы [ 3 ]

2 голосов
/ 21 апреля 2010

Invoke ничего не делает, потому что у вас не работает диспетчер. Чтобы получить какие-либо услуги от диспетчера, вам нужно вызвать Dispatcher.Run (http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run.aspx)

Теперь ваша проблема в том, что вызов Dispatcher.Run заставит WPF взять на себя управление потоком - и это, вероятно, не то, что вы хотите делать в консольном приложении.

Я думаю, что лучшим вариантом в вашей ситуации является удаление кода синхронизации потоков (всего, что говорит с диспетчером) из вашего бизнес-уровня и в объекты-оболочки.

Приложение WPF может продолжать работать, как и раньше, используя объекты-оболочки, а приложение консоли может напрямую использовать «необработанный» бизнес-уровень.

Обновление: ниже приведен пример оболочки, вам нужно создать метод / свойство для каждого открытого члена исходного класса, который выполняет работу с потоками и вызывает исходный объект.

public class BOWrapper : INotifyPropertyChanged
{
    private BO _bo;
    private Dispather _dispather;

    public BOWrapper(BO bo, Dispatcher dispather)
    {
        _bo = bo;
        _dispather = dispather;
        _bo.PropertyChanged += BOPropertyChanged;
    }

    public string SomeValue
    {
        get { return _bo.SomeValue; }
    }

    private void BOPropertyChanged(object sender, PropertyChangedEventArgs ea)
    {
        _dispatcher.Invoke(
            new Action<PropertyChangedEventArgs>(
                e=>
                {
                    var handler = PropertyChanged;
                    if(handler!=null) handler(this,e);
                }),ea);
    }
}

Класс-оболочка - это 100% шаблонный код, и вы, вероятно, можете использовать какой-то генератор кода для его создания, возможно, даже использовать что-то вроде DynamicProxy (http://www.castleproject.org/dynamicproxy/index.html) для его автоматической генерации во время выполнения.

0 голосов
/ 21 апреля 2010

Альтернатива: в потоке, в котором вы хотите использовать Dispatcher, используйте статическое свойство Dispatcher.CurrentDispatcher. Если диспетчер еще не был создан для потока, он создается автоматически (если это возможно). Вы можете хранить это значение где-нибудь, чтобы другие потоки могли использовать диспетчер.

В коде:

public class BusinessLayerThread
{
    public BusinessLayerThread()
    {
        Dispatcher = Dispatcher.CurrentDispatcher;
    }

    public static Dispatcher Dispatcher { get; private set; }
}
0 голосов
/ 21 апреля 2010

WPF в консольном приложении? В консольных приложениях нет ограничений на запуск функции в определенном потоке. Вы можете обрабатывать любое событие в контексте потока вызывающего, обеспечивая при необходимости синхронизацию.

...