Как открыть окно WPF в собственном потоке, а затем закрыть его? - PullRequest
1 голос
/ 17 сентября 2010

Я пытаюсь развлечь пользователя и показать окно «Пожалуйста, подождите» вместе с Marquee во время загрузки отдельного сложного окна.Я пытаюсь сделать это, загружая Окно в новом потоке, например:


    Public Function ShowPleaseWait() As System.Threading.Thread
        Dim PleaseWait As New System.Threading.Thread(AddressOf LoadPleaseWait)
        PleaseWait.SetApartmentState(System.Threading.ApartmentState.STA)
        PleaseWait.IsBackground = True
        PleaseWait.Start()

        Return PleaseWait
    End Function

    Public Sub LoadPleaseWait()
        Dim window As New windowPleaseWait
        Try
            window.Show()
            System.Windows.Threading.Dispatcher.Run()
        Catch e As System.Threading.ThreadAbortException
            window.Close()
            window = Nothing
        End Try
    End Sub 

В вызывающем коде он вызывает ShowPleaseWait и сохраняет поток на потом. Чтобы закрыть окно,он вызывает Thread.Abort в сохраненном потоке.Это, в свою очередь, заставит его ввести Catch.Я пробовал много разных способов, с уловом и без него.

Это работает невероятно, при первом вызове.Тем не менее, дополнительные вызовы не будут выполнены на window.Show(), за исключением: The calling thread cannot access this object because a different thread owns it..

Это действительно озадачивает меня, поскольку окно было создано на одну строку выше вызова на window.Show и является локальным.Как это принадлежит другой теме?Как я могу это исправить?

Ответы [ 3 ]

1 голос
/ 05 ноября 2010

Я вставил код, который вы показали здесь, и не могу воспроизвести проблему.(У него есть проблема, но она не вызывает проблему, которую вы описываете.) К сожалению, когда я пробую опубликованный вами код, я могу вызывать ShowPleaseWait столько раз, сколько мне нравится, и я не вижу исключения.

Должно быть, произошло одно из двух.Возможно, вы изменили код, упростив исходный пример, чтобы разместить что-то достаточно маленькое для публикации здесь, и в результате вы, возможно, также устранили проблему.(Если бы мне пришлось рисковать догадкой, было бы так, чтобы ваше Dim-окно как New windowPleaseWait изначально было членом класса, а не локальным, и поэтому вы в конечном итоге используете один и тот же объект окна каждый раз. Это единственное, что нужноЯ думал, что это объяснит описанные вами симптомы.)

В качестве альтернативы, я использую код в другом контексте, чем вы.

Чтобы исследовать последний, я объяснючто я делаю, чтобы опробовать ваш код, чтобы вы могли увидеть, если есть что-то явно отличное от контекста, в котором вы его запускаете. Я создал новое приложение WPF и вставил ваши методы в код MainHindow.xaml.vb,Затем я добавил кнопку в окно, и в обработчике кликов я вызвал ваш метод следующим образом:

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
    ShowPleaseWait().Join()
End Sub

Я вызываю Join в возвращенном потоке по двум причинам.Во-первых, я хотел заблокировать основной поток пользовательского интерфейса, чтобы убедиться, что вторичное окно действительно работает в отдельном потоке (что и было).Во-вторых, я хотел убедиться, что поток действительно завершил работу - отключение с прерыванием потока - это очень неортодоксальный способ сделать что-то.(Правильнее было бы закрыть диспетчер, который вы неявно создали в этом потоке, что позволило бы корректно завершить вызов Dispatcher.Run (). Это проблема, о которой я упоминал в начале.)

Чтобы попытаться сделать мой пример похожим на ваш, я закрываю дочернее окно, вызывая Thread.Abort, хотя это не очень хорошая идея - я делаю это в обработчике щелчков для кнопки на этом дочернем элементе.окно.(Мне пришло в голову, что проблема может возникнуть только при вызове Abort для этого дочернего потока из основного потока. Поэтому я также добавил пару кнопок, чтобы позволить мне сделать это таким образом. Без изменений - я все еще могу показать окно изатем уничтожайте поток столько раз, сколько мне нравится, даже не видя описанную вами ошибку.)

Таким образом, либо контекст, в котором вы используете этот код, является причиной его сбоя, либо код, который вы опубликовали.Здесь случайно была устранена проблема.В любом случае, боюсь, требуется больше информации.

1 голос
/ 15 марта 2017

Я даже не нашел проблем в вашем коде. Поместите эти методы в основной модуль.

Public Function ShowPleaseWait() As System.Threading.Thread
    Dim PleaseWait As New System.Threading.Thread(AddressOf LoadPleaseWait)
    PleaseWait.SetApartmentState(System.Threading.ApartmentState.STA)
    PleaseWait.IsBackground = True
    PleaseWait.Start()

    Return PleaseWait
End Function

Public Sub LoadPleaseWait()
    Dim window As New windowPleaseWait
    Try
        window.Show()
        System.Windows.Threading.Dispatcher.Run()
    Catch e As System.Threading.ThreadAbortException
        window.Close()
        window = Nothing
    End Try
End Sub

Теперь вызывайте функцию везде, где вам нужно, чтобы отобразить окно, как показано ниже.

Dim myThreadWindow as System.Threading.Thread 
myThreadWindow  = ShowPleaseWait()

Чтобы прервать поток.

myThreadWindow.Abort 
0 голосов
/ 17 сентября 2010

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

Я бы посоветовал вам изменить свой подход - выполнить «отдельную сложную» работу в фоновом потоке и убрать этот код из кода, управляющего окном. Использование таких шаблонов, как MVVM или фреймворков, таких как Prism, поможет в этом подходе.

Ваш поток должен быть примерно таким:

  • загрузить главное окно в пустом состоянии

  • начните работу с пользовательским интерфейсом загрузки данных (в вашем случае анимация «пожалуйста, подождите»)

  • запуск фонового потока

  • использовать этот фоновый поток для извлечения и / или манипулирования данными

  • после завершения фонового потока заполните вашу ViewModel или перенесите выполнение кода обратно в поток пользовательского интерфейса, чтобы заполнить пользовательский интерфейс

Еще раз, посмотрите на использование MVVM или аналогичного, это поможет выделить, где выполняется каждый из этих кусочков работы (то есть фоновый поток может выполняться в модели и никогда не приближаться к представлению).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...