Как открыть окно в новой теме?

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

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

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

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

Судя по другим вопросам, решение состоит в том, чтобы создать средство просмотра в новом потоке. Однако я сталкиваюсь с множеством проблем с этим.

Это срабатывает, когда я нажимаю кнопку, чтобы открыть средство просмотра:

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(): он обращается к диспетчеру средства просмотра, который знает, в каком потоке выполняется объект, и затем может вызывать методы из этого потока.

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

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


person mikey555    schedule 22.01.2012    source источник


Ответы (3)


Вам нужно вызвать Show() в том же потоке, в котором создано окно, поэтому вы получаете ошибку. Затем вам также необходимо запустить новый экземпляр 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();
}

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

person shf301    schedule 22.01.2012
comment
Я вижу, что Invoke гарантирует, что Show() вызывается в потоке просмотра. Даже без Invoke() вокруг viewer.Show() я все равно получаю исключение Freezable. - person mikey555; 22.01.2012
comment
@ michael.greenwald Тогда в SkeletalViewer.MainWindow() есть что-то, что вызывает исключение. Я создал пустой проект WPF, который делает именно то, что показано выше, и работает без исключений. Возможно, ваша проблема аналогична проблеме из этого вопроса: stackoverflow.com/questions/3636761/ - person shf301; 22.01.2012
comment
System.Windows.Threading существует в WindowsBase.dll, и вы должны добавить его в список ссылок. - person Think Big; 05.01.2018

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

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

person bwing    schedule 28.01.2014

Я не уверен, решит ли это вашу проблему, но можете ли вы попробовать создать поток 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
    } 

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

person DotNetUser    schedule 22.01.2012
comment
Спасибо. Почему вы отправляете цвет в поток пользовательского интерфейса? Не вызывается ли какой-либо вызываемый метод в потоке пользовательского интерфейса, потоке по умолчанию? - person mikey555; 22.01.2012
comment
потому что фоновый поток, который отделяется от основного потока пользовательского интерфейса, не может обновлять содержимое любого элемента управления, созданного в потоке пользовательского интерфейса. Чтобы фоновый поток мог получить доступ к элементу управления в главном окне, фоновый поток должен делегировать работу диспетчеру, связанному с потоком пользовательского интерфейса. Это достигается с помощью Invoke или BeginInvoke. Invoke является синхронным, а BeginInvoke — асинхронным. Операция добавляется в очередь событий Dispatcher с указанным DispatcherPriority. - person DotNetUser; 22.01.2012
comment
см. эту ссылку - msdn.microsoft.com/ ru/библиотека/ - person DotNetUser; 22.01.2012