2015-06-15 19 views
8
void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault) 
{ 
    var thread = new Thread(() => 
    { 
    if (LoginCheck()) 
    { 
     ItemWindow itw = new ItemWindow(); 
     //Dispatcher.CurrentDispatcher.Invoke((System.Action)(() => 
     //{ 
       itw.Show(); 
       itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 
     //})); 

     Dispatcher.Run(); 
    } 
    }); 

    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 

} 

Ciągle pojawia się komunikat o błędzie "Wątek wywołujący nie może uzyskać dostępu do tego obiektu, ponieważ inny wątek jest jego właścicielem." on-line "itw.show();" kiedy ta funkcja jest wywoływana dwa razy. Działa dobrze dla pierwszego połączenia, a po zamknięciu okna i próbie ponownego otwarcia nie działa. Ponieważ skomentowałem metodę "Invoke", nie działa ona również z Dispatcherem. Proszę, pomóż mi znaleźć rozwiązanie. Dziękuję.Tworzenie nowego okna WPF na nowym błędzie wątku

----------------- Edit

Powodem Tworzę nowy wątek, ponieważ jest to dodatek Excel. Nie mogę utworzyć okna z głównego wątku, który jest kodem Excel, który koliduje z oknami, jeśli utworzę je z głównego wątku.
Rzeczą, której nie rozumiem, jest to, dlaczego nowa instancja (ItemWindow) z nowego wątku koliduje ze starym wątkiem.

+2

Dlaczego chcesz utworzyć nowy wątek, aby wyświetlić to okno? Na ogół nie jest to dobry pomysł. Po drugie, próbowałem tego i działa dobrze (z wyjątkiem tego, że nie mam pojęcia, co robi "LoginCheck"). Gdzie występuje wyjątek? –

+2

Cały powiązany z UI kod musi działać w wątku głównym. Co próbujesz osiągnąć tutaj? – almulo

+0

Zwykle ten błąd odnosi się do próby uzyskania dostępu do właściwości w pierwszym formularzu od tego, który został zerwany. Musimy zobaczyć, co dzieje się w drugim formularzu, który powoduje problem. – DoomVroom

Odpowiedz

0

Stworzyłem prostą metodę testową w nowej aplikacji, która jest wywoływana po kliknięciu przycisku (tylko) w moim głównym formularzu. Metoda wygląda następująco:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    Thread thread = new Thread(() => 
    { 
     Window1 window = new Window1(); 
     window.Closed += (s, a) => window.Dispatcher.InvokeShutdown(); 
     window.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }); 

    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
} 

Window1 jest klasa okna zrobiłem, że nie ma nic, ale jednym TextBlock na nim. Mogę kliknąć ten przycisk tyle razy, ile chcę, i nadal otwiera nowe okna bez żadnych problemów (niezależnie od tego, czy najpierw zamknę poprzedni).

Podejrzewam, że problem występuje w kodzie, że nie pokazuje nam się gdzieś. Musisz być bardzo ostrożny, aby nic w nowym wątku nie próbowało uzyskać dostępu do dowolnego interfejsu użytkownika związanego z głównym wątkiem. System Windows działający w oddzielnych wątkach nie może komunikować się ze sobą, dopóki nie przejdzie przez moduł rozsyłający drugiego wątku. Wyjątek, który widzisz, jest generowany, gdy dowolna metoda lub właściwość obiektu DispatcherObject jest dostępna z wątku innego niż ten, który utworzył obiekt.

Cofając się, dlaczego ważne jest, aby nowe okno było w swoim wątku? O ile nowe okno nie zmonopolizuje wątku, prawdopodobnie będzie działało dobrze na głównym wątku. Jeśli wykonujesz jakąś długą operację blokowania, być może sama ta operacja powinna zostać przeniesiona do wątku, a nie do całego okna. Nie wiem, co dokładnie robisz, ale to jest coś do przemyślenia.


EDIT: Zdając sobie sprawę, że nie może być uruchomiony w typowej aplikacji WPF (wygląda na to, że możesz być w plugin Office), zaktualizowałem mój test do uruchomienia okna całkowicie samodzielnych na własnych wątkach. Jednak wciąż jestem w stanie uruchomić dwa okna z rzędu bez żadnych problemów.

Oto mój nowy test. Ta metoda i klasa testowa Window1 stanowią całość mojej aplikacji.

[STAThread] 
public static int Main(string[] args) 
{ 
    ThreadStart threadFunc =() => 
    { 
     Window1 window = new Window1(); 
     window.Closed += (s, a) => window.Dispatcher.InvokeShutdown(); 
     window.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }; 

    Thread thread = new Thread(threadFunc); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    thread.Join(); 

    thread = new Thread(threadFunc); 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 
    thread.Join(); 

    return 0; 
} 

Tak, wydaje się, że nic złego z tym, co staramy się robić, nie widzę żadnych oczywistych kwestii w kodzie. Podejrzewam, że w oknie niestandardowym pojawia się jakaś nieważna komunikacja między wątkami, gdy jest wyświetlana. (Albo to albo występuje problem specyficzny dla wtyczek pakietu Office).

+0

Myślę, że to ma coś wspólnego z programem excel addin. Nie rozumiem, dlaczego nowa instancja (ItemWindow) z nowego wątku koliduje ze starym wątkiem. – icewall

+0

Czy obejrzałeś stację wywoławczą wyjątku? Być może pokaże kilka wskazówek na temat tego, co robi nieprawidłowe połączenie. Możesz także wyłączyć opcję debugowania "Just My Code", aby wyświetlić pełną listę odbierającą (włącznie z metodami z zewnętrznych złożeń) w oknie Call Stack w Visual Studio. – Xavier

0

Próbujesz połączyć program obsługi zdarzeń z obiektem ItemWindow po, który jest już widoczny.

Trzeba zmienić kolejność od:

ItemWindow itw = new ItemWindow(); 
itw.Show(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 

do

ItemWindow itw = new ItemWindow(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); }; 
itw.Show(); 
-1

Możliwą przyczyną są właściwości zależnościami. Właściwości zależności są nieco wybredne, jeśli chodzi o wątki.

Nawet jeśli nie zdefiniujesz własnych DepProps, twoje okno nadal będzie dostępne i nie ma sposobu, aby się ich pozbyć.

DepProps mają znaczącą wadę: są powiązane z wątkami i nie można uzyskać do nich dostępu z innego wątku. Który wątek zawiera wszystkie prawa jest określony przez wątek, który inicjuje DepProps, w twoim przypadku pierwsze połączenie z new ItemWindow(). Po tym pierwszym połączeniu twój wątek jest ustawiony i potrzebujesz tego wątku, aby uzyskać dostęp do twoich DepProps.

Dla pierwszego okna nie ma problemu, ale drugi wyraźnie ma inny wątek. Nie wiem dokładnie jak to robi DepProps, ale możesz spróbować przechwycić i przywrócić kontekst synchronizacji pierwszego wątku. Inną opcją byłoby przechwycenie dyspozytora pierwszego wątku (nie głównego wątku).