2012-01-22 5 views
8

У меня есть окно параметров и окно, которое отображает цвет на основе этих параметров и данных Kinect. Пока все на одном потоке (насколько я знаю, я не делал ниток).Как открыть окно в новом потоке?

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

viewer = new SkeletalViewer.MainWindow(); 
viewer.Show(); 

Когда это событие происходит, окно цвета перестает отображать цвета (то есть событие, которое срабатывает в 30 раз в секунду на основном потоке прекращает стрельбу), но зритель отображается отлично. Я хочу, чтобы зритель и окно цвета были обновлены.

Из чтения других вопросов, похоже, решение состоит в том, чтобы создать зрителя в новом потоке. Тем не менее, я столкнулся с множеством проблем.

This пожары, когда я нажимаю на кнопку, чтобы открыть зрителю:

private void launchViewerThread_Click(object sender, RoutedEventArgs e) 
    { 
     Thread viewerThread = new Thread(delegate() 
     { 
      viewer = new SkeletalViewer.MainWindow(); 
      viewer.Dispatcher.Invoke(new Action(delegate() 
       { 
        viewer.Show(); 
       })); 
     }); 

     viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception 
     viewerThread.Start(); 

    } 

Независимо от того, если я просто называю viewer.Show() или Invoke() это, как указано выше, линия бросает исключение: Не может используйте объект DependencyObject, который принадлежит другому потоку, чем его родительский Freezable. Вот как я понимаю Invoke(): он обращается к диспетчеру зрителя, который знает, в какой поток работает объект, и затем может вызывать методы из этого потока.

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

Кто-нибудь знает, почему это не работает? Спасибо за помощь.

ответ

15

Вам нужно позвонить Show() в том же потоке, что окно i созданный на - вот почему вы получаете ошибку. Затем вам также нужно запустить новый экземпляр Dispatcher, чтобы заставить среду выполнения управлять окном.

private void launchViewerThread_Click(object sender, RoutedEventArgs e) 
{ 
    Thread viewerThread = new Thread(delegate() 
    { 
     viewer = new SkeletalViewer.MainWindow(); 
     viewer.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }); 

    viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception 
    viewerThread.Start(); 
} 

См Multiple/Несколько потоков пример для Windows по адресу: http://msdn.microsoft.com/en-us/library/ms741870.aspx

+0

Я вижу Invoke как гарантию того, что Show() вызывается в потоке зрителя , Даже без Invoke() вокруг viewer.Show(), я все еще получаю исключение Freezable. –

+0

@ michael.greenwald Тогда в 'SkeletalViewer.MainWindow()' есть что-то, что вызывает исключение. Я построил пустой проект WPF, который делает то, что я показываю выше, и работает без исключения. Возможно, вы выпускаете аналогично проблеме из этого вопроса: http://stackoverflow.com/questions/3636761/how-to-debug-this-error-when-none-of-my-code-shows-up-in- the-stack – shf301

+0

И как закрыть это окно из 'launchViewerThread'? –

0

Я не уверен, что это решит вашу проблему, но вы можете попробовать создать поток proc (чтобы открыть окно просмотра), который выполняется в другом потоке, а затем dispatcher.beginInvoke для обновления главного окна,

Вот некоторые код-

in the constructor register this 
    public MainWindow() 
    { 
     UpdateColorDelegate += UpdateColorMethod; 
    } 

    // delegate and event to update color on mainwindow 
    public delegate void UpdateColorDelegate(string colorname); 
    public event UpdateColorDelegate updateMainWindow; 

    // launches a thread to show viewer 
    private void launchViewerThread_Click(object sender, RoutedEventArgs e) 
    { 
     Thread t = new Thread(this.ThreadProc); 
     t.Start(); 
    } 

    // thread proc 
    public void ThreadProc() 
    { 
     // code for viewer window 
     ... 
     // if you want to access any main window elements then just call DispatchToMainThread method 
     DispatchToUiThread(color); 
    } 

    // 
    private void DispatchToUiThread(string color) 
    { 
     if (updateMainWindow != null) 
     { 
     object[] param = new object[1] { color}; 
     Dispatcher.BeginInvoke(updateMainWindow, param); 
     } 
    } 

    // update the mainwindow control's from this method 
    private void UpdateColorMethod(string colorName) 
    { 
     // change control or do whatever with main window controls 
    } 

при этом вы можете обновить основные элементы управления окна без замораживания его, дайте мне знать, если у вас есть какие-либо вопросы

+0

Спасибо. Почему вы отправляете цвет в поток пользовательского интерфейса? Не вызван ли какой-либо вызываемый метод в потоке пользовательского интерфейса, поток по умолчанию? –

+0

, потому что фоновый поток, выделенный из основного потока пользовательского интерфейса, не может обновлять содержимое любого элемента управления, созданного в потоке пользовательского интерфейса.Чтобы фоновый поток мог получить доступ к элементу управления в главном окне, фоновый поток должен делегировать работу диспетчеру, связанному с потоком пользовательского интерфейса. Это достигается с помощью Invoke или BeginInvoke. Invoke является синхронным, а BeginInvoke является асинхронным. Операция добавляется в очередь событий Диспетчера в указанном DispatcherPriority. – DotNetUser

+0

см. Эту ссылку - http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.begininvoke.aspx – DotNetUser

1

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

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

Смежные вопросы