2013-05-06 7 views
8

Tworzę plik SolidColorBrush w przypadku niektórych wątków innych niż GUI i chcę przekazać go do wątku GUI, aby go wyświetlić, ale otrzymuję InvalidOperationException: The calling thread cannot access this object because a different thread owns it. (nawet jeśli spróbuję uzyskać Freeze();). Jak przekazać obiekt utworzony w wątku X do wątku Y?Jak przekazać obiekt WPF, który został utworzony w wątku X, do wątku Y?

Wiem, że mogę utworzyć ten obiekt SolidColorBrush w wątku GUI z Dispatcher, ale to wszystko komplikuje ... Chcę go utworzyć w wątku roboczym.


Dodatkowe szczegóły:

zainicjować jakąś statyczną delegata w jakimś statycznym klasy, aby umożliwić wysyłanie wiadomości z warstwy biznesowej do GUI:

public static class Gui{ 
    private static PrintMethodDelegate _printMethod; 
    public static void InitializeGuiInterface(PrintMethodDelegate printMethod){ 
     _printMethod = printMethod; 
    } 
    public static void Print(GuiMessage data) { _printMethod(data); } 
} 

inicjalizacji (w wątku GUI):

Gui.InitializeGuiInterface(_messagesToUserHandler.PrintMessage); 

Następnie w innym wątku (bez GUI) używam go:

Gui.Print(new GuiMessage(testDescription) { Foreground = new SolidColorBrush(someColor) }); 

podczas GuiMessage jest:

public class GuiMessage { 
    public string Msg { get; set; } 

    private SolidColorBrush _foregroundBrush; 
    public SolidColorBrush Foreground 
    { 
     get { return _foregroundBrush; } 
     set { _foregroundBrush = value; } 
    } 
} 
+0

Czy próbowałeś napisać komunikat o błędzie do google? Zwraca wszystkie odpowiedzi, które możemy tutaj podać. – I4V

+1

Myślę, że krótka odpowiedź brzmi, że nie możesz. Wszystkie kontrolki/artefakty związane z GUI powinny być tworzone i obsługiwane w wątku UI. –

+0

@ I4V: Tak, próbowałem. Dziękuje za twoją sugestię. – Tar

Odpowiedz

6

Możesz utworzyć zasoby wpf w innym wątku, jeśli je freeze, po czym element może zostać przekazany do kolejnego wątku lub wątku GUI. Pamiętaj, że jednorazowy zamrożony obiekt można zmodyfikować tylko wykonując kopię i używając tej kopii. Nie można zamrażać obiektów powiązanych z powiązaniami lub animacjami.

+0

To wszystko. Natychmiast zamrażam 'Pędzel' w ustawiaczu właściwości. Więc nie jest to ograniczenie wielowątkowe ... zostało zaprojektowane w ten sposób dla tego rodzaju obiektów ('Freezable's) – Tar

1

Trzeba użyć delegatem na bezpieczne powołać się na kontrolę.

Zastosowanie

Control.Invoke

lub

Control.BeginInvoke

do tego celu.

Jeśli nie użyjesz delegata do bezpiecznego wywołania, otrzymasz wyjątek.

Sprawdź te linki:

How to update the GUI from another thread in C#?enter link description here

enter link description here

+0

Widziałem to, ale to nie pomoże mi, jeśli 'Pędzel' został stworzony w wątku bez GUI w pierwszej kolejności. Rozwiązuję tylko problem, w którym chcesz wywołać obiekt utworzony w wątku GUI, a nie odwrotnie. – Tar

0

Należy użyć Dispatcher.

Możesz utworzyć klasę, która pomieści dyspozytora utworzonego w głównym wątku i wstrzyknie go przez twój kontener do klasy wykonanej na wątku tła, który musi wejść w interakcję z głównym wątkiem.

public interface IUiDispatcher 
{ 
    Dispatcher Dispatcher { get; } 
} 

public class UiDispatcher : IUiDispatcher 
{ 
    public UiDispatcher() 
    { 
     if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA 
      && !Thread.CurrentThread.IsBackground 
      && !Thread.CurrentThread.IsThreadPoolThread) 
     { 
      this.Dispatcher = Dispatcher.CurrentDispatcher; 
     } 
     else 
     { 
      throw new InvalidOperationException("Ui Dispatcher must be created in UI thread"); 
     } 
    } 

    public Dispatcher Dispatcher { get; set; } 
} 

public class ExecutedOnABackgroundThread 
{ 
    IUiDispatcher uidispatcher; 

    public ExecutedOnABackgroundThread(IUiDispatcher uidispatcher) 
    { 
     this.uidispatcher = uidispatcher; 
    } 

    public void Method() 
    { 
     // Do something on the background thread... 
     // ... 

     // Now we need to do something on the UI 
     this.uidispatcher.Dispatcher.BeginInvoke(new Action(delegate 
     { 
      // Do something 
     }), null); 
    } 
} 

Utwórz instancję UiDispatcher w punkcie Twoje są pewni swojej są na wątku UI, na przykład podczas inicjalizacji aplikacji.Korzystając z kontenera wtryskowego zależności, upewnij się, że tylko jedna instancja tej klasy zostanie utworzona i wstrzyknięta do dowolnej innej klasy, która tego wymaga, i użyć go do utworzenia/manipulowania składnikami interfejsu użytkownika.

Wybrałem kod, aby sprawdzić, czy konstruktor UiDispatcher jest wykonany w głównym wątku z this answer.

Chodzi o to, że nie można używać w wątku UI czegoś stworzonego na innym wątku. Potrzebujesz więc wątku w tle, aby delegować do głównego wątku UI, co dotyczy elementów interfejsu użytkownika.

+0

Tak jak powiedziałem: "Wiem, że mogę utworzyć ten obiekt' SolidColorBrush' w wątku GUI z 'Dispatcherem', ale to wszystko komplikowałoby ... Chcę go utworzyć w wątku roboczym" – Tar

+0

Tak, przeczytałem. Ale nie wiem, co jest skomplikowane. Zwłaszcza, jeśli musisz po prostu użyć 'this.dispatcher.BeginInvoke'. W jaki sposób można użyć w wątku interfejsu użytkownika czegoś utworzonego w wątku tła? [Nie ma innego sposobu, niż delegowanie kreacji do wątku UI] (http://www.google.com.sg/#output=search&sclient=psy-ab&q=+wstrzymanie+starego+kucha+na+wystąpieniu+w+obiekcie+ ponieważ + a + inny + wątek + posiada + to). – Guillaume

+0

Przeszukałem to i pomyślałem, że to niedorzeczne, że nie da się tego zrobić. Nie możesz skopiować danych z jakiegoś segmentu pamięci do innego segmentu pamięci? klonować obiekt? to nie ma sensu. Dlaczego to ograniczenie istnieje? – Tar

Powiązane problemy