2012-11-20 7 views
5

Jestem bardzo zdezorientowany tym i zaczyna mnie kwestionować całe moje rozumienie systemu zasobów WPFDlaczego pakiety Freezables są już zamrożone i mają pustego Dispatchera (to samo ze stylami), gdy są przechowywane w Application.Resources?

Mam aplikację z wieloma oknami, gdzie każdy obiekt pochodzący z okna działa w oddzielnym wątku z oddzielnym modułem dyspozytorskim.

Thread t = new Thread(() => { 
    Window1 win = new Window1(); 
    win.Show(); 
    System.Windows.Threading.Dispatcher.Run(); 
}); 
t.SetApartmentState(ApartmentState.STA); 
t.Start(); 

Mam słownika zasobu Dictionary1.xaml o nazwie obiektu Style w środku (to po prostu ustawia właściwość tła na czerwono i jest kierowane na TextBox). W moim App.xaml odwołuję się do Dictionary1.xaml za pośrednictwem kolekcji ResourceDictionary.MergedDictionaries. W XAML innych moich okien mam StaticResource do klucza stylu w formancie textbox, który działa.

Jestem w stanie otworzyć wiele okien, ale czy nie powinienem otrzymywać błędów związanych z przecinaniem? W konstruktorze jednej z klas okiennych Zrobiłem to:

Style s = (Style)TryFindResource("TestKey"); 
Console.WriteLine(((Setter)s.Setters[0]).Property.Name); // no problem 
s.Dispatcher == this.Dispatcher // false 

ponieważ obiekt Style pochodzi od DispatcherObject, nie znaczy, że to jest dostępne tylko dla wątku, który jest jej właścicielem? A jeśli obiekt jest zdefiniowany w ResourceDictionary, nie oznacza to, że domyślnie jest to instancja statyczna? Jak to działa? Dlaczego nie otrzymuję błędu krzyżowego?

(I błędnie podano pytanie ja od usuniętego o przekroju błędu gwintowania, który został spowodowany przez coś innego)

Jestem bardzo zmieszany przez to - myślałem tylko mrożone freezable obiekty były współdzielone drugiej nici. Dlaczego mam dostęp do obiektu DispatcherObject w innych wątkach?

Odpowiedz

4

Więc mam odpowiedź w końcu - ja wykopane przez alot kodu .NET Framework i doszła do następujących wniosków:

Kiedy słownika zasób jest albo słownika na poziomie aplikacji, słownik tematem lub do odczytu tylko wszystkie elementy przechowywane w słowniku zasobów zostaną „zamknięte”

if (this.IsThemeDictionary || this._ownerApps != null || this.IsReadOnly) 
{ 
    StyleHelper.SealIfSealable(value); 
} 

... 

internal static void SealIfSealable(object value) 
{ 
ISealable sealable = value as ISealable; 
if (sealable != null && !sealable.IsSealed && sealable.CanSeal) 
{ 
    sealable.Seal(); 
} 
} 

„Uszczelnienie” obiekt zasadniczo czyni go niezmienny i jest realizowany poprzez słownika ISealable - w rzeczywistości Freezable implementuje to zachowanie uszczelnienia poprzez wywołanie zamarznięcia() ! Style również ją implementują, a jej implementacja uniemożliwia modyfikację kolekcji Setters lub Triggers. ISealable jest realizowane przez wiele klas!

public abstract class Freezable : DependencyObject, ISealable 
public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient 

Co ważniejsze, każda realizacja Seal() w każdej klasie WPF widziałem (jak dotąd) wywołuje DispatcherObject.DetachFromDispatcher(), która ustawia dyspozytora na null! (Freeze() wewnętrznie to również nazywa)

"Plombowanie" wydaje się implementować zachowanie niezmienności, które często jest reklamowane jako wyłączne dla Freezable, ale obiekty implementujące ISealable będą wykazywać to samo zachowanie i będą bezpieczne dla wątków - Freezables mają dodatkowe zachowanie, które je odróżnia (np. zmiana powiadomienia podmenu)

Podsumowując, "zamknięcie" obiektu pozwala w efekcie na współdzielenie wątków, ponieważ każdy obiekt jest automatycznie odłączany od dyspozytora, jeśli jest obecny w słowniczku zasobów na poziomie aplikacji lub kompozycji lub w słowniku tylko do odczytu

Aby zweryfikować ten wniosek, należy rozważyć ver ResourceDictionary i zobacz metodę AddOwner(), która ustawia logiczny element nadrzędny ResourceDictionary (np. FrameworkElement, FrameworkContentElement lub Application). Dlatego pędzle i obiekt Style były dostępne z innych wątków, ponieważ słowników zasobów były połączone w Application.Resources i w ten sposób wszystkie są automatycznie zapieczętowane - nie dziwi więc, że umieszczenie tych zasobów na poziomie Window spowoduje zwykły wyjątek cross-threading.

Domyślam się, że można uogólnić, że ISealable to to, co umożliwia udostępnianie obiektów WPF przez wątki i tylko do odczytu, chociaż techniczne odłączenie od Dispatchera nie jest wymagane przez protokół (tak jak DispatcherObjects nie są wymagane do wykonania VerifyAccess () wywołuje w każdej nieruchomości), ponieważ technicznie jest do każdego obiektu do realizacji jego własne zachowanie Seal() i nie ma bezpośredniej korelacji między ISealable i Dispatcher

2

Jestem również bardzo zdezorientowany z powodu "problemu". Nie ma magii z obiektem wywołującym, powinowactwo wątku powinno być zakodowane w klasie dziedziczącej z DispatcherObject: Każdy akcesor, który nie powinien być wielowątkowy, powinien wywołać metodę base.VerifyAccess() dostarczoną przez DispatcherObject. A oto sposób getter własności Style.Setters jest zdefiniowana:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
public SetterBaseCollection Setters 
{ 
    get 
    { 
     base.VerifyAccess(); 
     if (this._setters == null) 
     { 
      this._setters = new SetterBaseCollection(); 
      if (this._sealed) 
      { 
       this._setters.Seal(); 
      } 
     } 
     return this._setters; 
    } 
} 

Więc w twoim przypadku wyjątek powinien rzeczywiście zostały rzucone ...

Może spróbować wywołać s.CheckAccess() metoda. Można również wypróbować porównanie metodami Dispatcher Object.ReferenceEquals (A, B), aby upewnić się, że operator równości nie został przeciążony (chociaż nie znalazłem żadnego przeciążenia ...).

Ten temat jest bardzo interesujący, informuj nas na bieżąco!

+0

Tak, próbowałem CheckAccess i zwrócił true, podczas gdy VerifyAccess nie rzucił wyjątku . ReferenceEquals również zwraca wartość przewidywalną. Dzięki za przypomnienie mi, że DispatcherObject nie jest również obiektem magicznym, a powinowactwo wątku jest wymuszane przez ręczne wywoływanie do VerifyAccess() - świetny punkt/przypomnienie! Bardzo zdezorientowany – blue18hutthutt

+1

Jako kolejny test, właśnie stworzyłem w słowniku zasobów. Powinno to stworzyć niezamrożoną nową SolidColorBrush, która z definicji NIE powinna być dostępna dla innego wątku, a jednak ... jest ORAZ do czasu, kiedy ją odzyskasz z innego okna - jest już zamrożona! Moje rozumienie, jak myślałem, że WPF pracował po prostu spadł ... – blue18hutthutt

+1

Oto kod metody Dispatcher.CheckAccess(): "return this.Thread == Thread.CurrentThread;" "this.Thread" jest naturalnie instancją wątku przechowywaną w obiekcie instanciation. Czy możesz potwierdzić, że ten test również zwraca się do Ciebie? –

0

Nicea pytanie, istnieją dwa punkty chciałbym wspomnieć,

Pierwszy punkt jest, za każdym razem poprosić o styl to dadzą Ci nowy obiekt w stylu tak nigdy obiekty w stylu są dzielone pomiędzy wiele elementów sterujących, ponieważ styl jest podobny do części klasy zawierającej informacje o stylu.

Po drugie, dlaczego dyspozytor powinien wyrzucić wyjątek, ponieważ dyspozytor zawsze działa na jednym wątku. Tak więc, gdy zmienisz kontroler kontrolki, reguła koligacji wątku korzystasz z programu rozsyłającego i renderujesz rzeczy w wątku GUI.

Czy ta informacja pomaga.

+1

To nie jest poprawne - każdy obiekt, który jest utworzony w kolekcji Zasoby, jest domyślnie udostępniony. Zweryfikowałem to właśnie wtedy, dodając styl do globalnego obiektu pamięci podręcznej na początku, a następnie wykonując porównanie ReferenceEquals z konstruktorem jednej z klas okna potomnego i są one takie same (x: Shared = "false", aby je oddzielić). Myślę, że powinien zostać zgłoszony wyjątek, ponieważ każde okno działa w oddzielnym wątku, a ja również uruchamiam osobny moduł rozsyłający w każdym wątku, dlatego obiekt stylu powinien być niedostępny bez zestawiania – blue18hutthutt

+0

to zależy od użycia, ale styl jest zawsze podobny to .. możesz użyć jednego stylu w wielu zasobach. a jeśli zmiana stylu nie oznacza, że ​​zmieni się we wszystkie miejsca, w których jest stosowana. jeśli to nie jest twoja sprawa, to może być inaczej. –

+0

Cóż, wszystko zależy od tego, gdzie zdefiniowany jest styl - jeśli jest on przechowywany w oknie. Źródła dla każdej z moich niestandardowych klas okien, to byłby oddzielnym obiektem stylu dla każdej instancji okna, jednak jeśli są zdefiniowane w App.xaml, będzie tym samym obiektem stylu – blue18hutthutt

Powiązane problemy