2012-01-27 8 views
29

Szukałem na this question i zauważył, że umieszczenie niejawny TextBlock styl Application.Resources dotyczy tego stylu do wszystkich TextBlocks, nawet tych wewnątrz innych elementów sterujących, takich jak Buttons, ComboBoxes itpNiejawne style w Application.Resources vs Window.Resources?

<Application.Resources> 
    <Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="Foreground" Value="Blue" /> 
    </Style> 
</Application.Resources> 

Umieszczenie niejawny styl Window.Resourcesdoes not cross control template boundaries , więc rzeczy takie jak Buttons izachowują swój domyślny czarny tekst.

<Window.Resources> 
    <Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="Foreground" Value="Blue" /> 
    </Style> 
</Window.Resources> 

Ponadto, dodając domyślny styl w Application.Resources sprawia, że ​​tak nie można nadpisać ten styl z innym niejawny stylu.

<!-- Doesn't work if implicit style with same property is in Application.Resources --> 
<ComboBox.Resources> 
    <Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="Foreground" Value="Red" /> 
    </Style> 
</ComboBox.Resources> 

Moje pytania są następujące:

  • Dlaczego tak jest?
  • Czy istnieją inne różnice między Application.Resources i Windows.Resources?
  • Kiedy należy używać jednego nad drugim?

    Rozumiem, że Application.Resources zastosowanie do całej aplikacji, a Window.Resources dotyczy tylko okna, ale chcę wiedzieć, dlaczego style Application są traktowane inaczej niż stylów w Window

+0

Dobre pytanie i wgląd na temat, w jaki sposób niejawne style App.xaml przekraczają granice ControlTemplate. Z tego powodu importuję/łączę niektóre słowniki zasobów do * każdego * moich okien aplikacji (tak naprawdę właśnie importuję pojedynczy plik master rd). Czy znalazłeś lepszy sposób radzenia sobie z tym problemem, niż jak to robię? (Byłoby ładniej, gdybyśmy mogli po prostu zaimportować go jednorazowo - tak jak robimy to z importowanymi zasobami App.xaml.) –

+0

Ten problem z przekroczeniem granicy ControlTemplate jest szczególnie kłopotliwy w przypadkach takich jak domniemany (App.xaml-level) Styl TextBlock, w którym TextBlock zostaje automatycznie wygenerowany jako element podrzędny elementu ContentPresenter formantu. Używanie sztuczki na poziomie okna, o której wspomina Rachel, rozwiązało dla mnie ten problem. –

+0

@Jason Zazwyczaj moje aplikacje WPF mają tylko jedno okno, a zawartość zmienia się w razie potrzeby. Nie sądzę, żebym kiedykolwiek zrobił coś więcej niż dwa okna (logowanie i aplikacja). Powiedział, że prawdopodobnie można użyć MEF do importowania/eksportowania słowników zasobów, jak wyjaśniono [tutaj] (http://stackoverflow.com/q/842571/302677) – Rachel

Odpowiedz

22

To naprawdę jedyna specjalna obsługa dodana do WPF i została wykonana według projektu.Kod, który realizuje to można znaleźć w FrameworkElement w sposobie FindImplicitStyleResource, który skutecznie służy:

internal static object FindImplicitStyleResource(FrameworkElement fe, object resourceKey, out object source) 
{ 
     // ... 
     DependencyObject boundaryElement = null; 
     if (!(fe is Control)) 
      boundaryElement = fe.TemplatedParent; 
     // ... 
} 

więc zasadą jest to, że utajone Style są zawsze dostarczane do kontrolnej (tj wywodzi Control). Zakładając, że można znaleźć ukryty styl.

W przypadku elementów używanych w modelu ControlTemplate, które nie pochodzą od Control, takich jak TextBlock, domyślne wyszukiwanie stylu nie spowoduje przekroczenia tego elementu macierzystego na szablonie. W twoim przypadku byłby to ComboBox.

Sądzę, że zostało to zrobione w taki sposób, że domyślne style dla TextBlock nie zostały nieumyślnie zastosowane do elementów TextBlock używanych w szablonach kontrolnych, które programista mógł lub nie wiedział, że tam były. Niejawne style zostaną zastosowane tylko do TextBlocks faktycznie utworzonych przez programistę w ich własnym XAML.

Aplikacja niejawna Style nadal umożliwiają globalną stylizację, na przykład zwiększenie rozmiaru czcionki. Ale prawdopodobnie spowodowało więcej zamieszania niż jest warte.

Nie ma dobrej odpowiedzi, aby powiedzieć, kiedy używać jednej z drugą, ponieważ każda z nich ma swoją funkcję. Oczywiście, jeśli nie chcesz wpływać na każdą aplikację TextBlock, nie powinieneś umieszczać stylu w zasobach aplikacji.

Należy jednak pamiętać, że ma to wpływ na element inny niż Control, na przykład elementy Shape.

+1

Czy wiesz, dlaczego niejawny styl w 'Application.Resources' przesłania inny styl niejawny zdefiniowany w' Zasobach' dowolnego innego elementu interfejsu użytkownika? – Rachel

+1

@Rachel - Nie wierzę, że to prawda. W przypadku kontrolek domniemany styl zdefiniowany w Window.Resources ma pierwszeństwo przed tymi w Application.Resources. W przypadku UIElements, które nie pochodzą od Control i które są używane w szablonie, Windows.Resources nie jest pytany, więc w takim przypadku zostanie użyty Application.Resources. – CodeNaked

+0

Masz rację, to tylko elementy interfejsu użytkownika, które nie pochodzą od "Kontroli", które i tak nie dziedziczą z niejawnych stylów. Dziękuję Ci :) – Rachel

1

Całkiem równinie tak proste

Jeśli chcesz Resources dzielony pomiędzy całej aplikacji należy użyć Application.Resources

Jeśli Zasoby chcesz być współużytkowane przez całe okno byłoby użyć Window.Resources

Jeśli chcesz Resources dzielony pomiędzy jednym kontroli byś wykorzystać (Cokolwiek Control) .Resources

Powiedzmy, że masz wiele okien ale chcesz tylko domyślny styl w jednym, ale nie inny wtedy użyć Windoe.Resources

+1

Ale jestem zdezorientowany, dlaczego ustawienie niejawnego stylu 'TextBlock' w' Application.Resources' przekroczy granice kontrolne, gdy domyślne zachowanie jest dla niejawnych stylów, aby nie krzyżowały się z szablonami kontrolnymi, chyba że element, do którego zastosowano styl niejawny, jest wpisz "Control". Do tej pory wszystkie moje aplikacje WPF były zawarte w jednym oknie, więc zawsze używam 'Window.Resources' – Rachel

+2

Osobiście unikam niejawnych stylów, ponieważ wprowadzają one w błąd projektantów. Powiedzmy, że masz 100k linii kodu lub więcej z ponad 20 słowników zasobów połączonych w App.xaml i dodaję niejawny styl w jednym z nich. Powiesz sobie, jeśli nigdy nie widziałeś kodu, CO SIĘ NASTĄPI! Widzę używanie implikowanego stylu w może niestandardowym USerControl, gdzie jest to test lub quiz lub coś, gdzie każdy TextBlock może mieć taki sam margines. – MyKuLLSKI

+0

Często używam niejawnych stylów, ponieważ jestem leniwy i nie lubię definiować nazwanych stylów dla absolutnie wszystkiego.Często zdarza się, że domyślne style definiują margines, wyrównanie lub kolory ogólnych elementów sterujących u góry widoku, chociaż teraz, gdy o tym myślę, często nie stosuję niejawnych stylów w całej aplikacji w oknie. Zasoby 'lub' Application' (oczywiście niejawne DataTemplates to inna historia) – Rachel

0

różnica leży w zakresie stylów:

  • po umieszczeniu w Application.Resources, styl będzie stosuje się do wszystkich kontroli w a TOSOWANIE
  • po umieszczeniu wewnątrz Windows.Resources, styl będą miały zastosowanie do wszystkich elementów sterujących w oknie

różnica jest dość subtelna tam, ale co to znaczy, że cokolwiek kontrola mówimy (w zestawie jeden to w innym szablonie kontrolnym) otrzyma styl z aplikacji. Źródła. ale tylko kontrolki bezpośrednio potomne okna otrzymają styl z okna. Źródła. Kontrolka wewnątrz szablonu kontrolki antoher nie będzie miała stylu zdefiniowanego w oknie. Odsyłacze, ponieważ nie znajduje się on bezpośrednio w oknie, natomiast będzie miał styl zdefiniowany w Application.Resources, ponieważ znajduje się on w aplikacji.

jak dla drugiego punktu, że ma do czynienia z nieruchomości zależność pierwszeństwa myślę:

http://msdn.microsoft.com/en-us/library/ms743230.aspx

+1

To nie prawda. Jeśli masz 'Button' w' ControlTemplate' innego 'Control' (np.' TextBox'), to każdy niejawny Styl dla 'Button' w Windows.Resources będzie nadal stosowany. – CodeNaked

0

Rachel, nie sądzę, nie ma nic szczególnego do „Style”. Co więcej, nie ma problemu "przekraczania granic szablonów". Powód tego jest inny i przechodzi do różnych "drzew" w aplikacji WPF. Poprzez swoje pytanie ja recon jesteś wyobrażając sobie świat z następującą hierarchią:
- Zastosowanie => => Okno kontrolne => Elementy będące pod kontrolą

Nie ma takiej hierarchii. W aplikacji WPF znajdują się różne drzewa, najbardziej znane to Drzewo logiczne i Drzewo wizualne, ale jest ich więcej (drzewo zdarzeń routingu, a także drzewo odnośników zasobów, z nieco inną semantyką).

Przyjmijmy następujące XAML:

<Window x:Class="SO.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Button x:Name="btn" Click="click">Click Me</Button> 
    </Grid> 
</Window> 

dla tej XAML, logicznym drzewo będzie wyglądać następująco:
- Window => Siatka => Przycisk => String

TextBlock wewnątrz przycisku jest nie jest częścią logicznego drzewa (jest to jednak część VisualTree).

Wyszukiwanie zasobów odbywa się według LogicalTree, z jedną różnicą. Po dotarciu do obiektu najwyższego, algorytm zasobów odnajdywania będzie przeglądać słownik zasobów Aplikacja, a następnie w schemacie zasobów zasobu , a następnie w słowniku zasobów System w tej kolejności.

patrz poniższe artykuły:

Finnaly, aby udowodnić swój punkt , dodaj następujący zasób do aplikacji XAML:

<Application x:Class="SO.App" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:clr="clr-namespace:System;assembly=mscorlib" 
    StartupUri="MainWindow.xaml"> 
    <Application.Resources> 
     <clr:String x:Key="MyResource">Hello Application Resource</clr:String> 
    </Application.Resources> 
</Application> 

i następujący kod za:

private void click(object sender, RoutedEventArgs e) 
{ 
    // Logical Children of btn 
    Debug.WriteLine("Children of btn:"); 
    foreach(var x in LogicalTreeHelper.GetChildren(btn)) { 
     Debug.WriteLine("{0} : {1}", x, x.GetType()); 
    } 

    // Walk the visual tree 
    Debug.WriteLine("The Visual Tree:"); 
    WalkVisual(0, this); 

    // Find the textblock within the button 
    DependencyObject p = btn; 
    while (p.GetType() != typeof(TextBlock)) 
     p = VisualTreeHelper.GetChild(p, 0); 
    TextBlock tb = p as TextBlock; 

    // Now climp the textblock through the logical tree 
    while (p != null) 
    { 
     Debug.WriteLine("{0}", p.GetType()); 
     p = LogicalTreeHelper.GetParent(p); 
    } 

    // Find a resource for the textbox 
    string s = tb.FindResource("MyResource") as string; 
    Debug.WriteLine("MyResource Content: {0}", s); 
} 

private void WalkVisual(int indent, DependencyObject p) 
{ 
    string fmt = string.Format("{{0,{0}}}{{1}}", indent * 4); 
    Debug.WriteLine(fmt, "", p.GetType()); 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(p); ++i) 
    { 
     WalkVisual(indent+1,VisualTreeHelper.GetChild(p, i)); 
    } 
} 

Więc ... po zrozumieniu pierwsze pytanie („dlaczego tak jest”), pozostałe pytania rozpadnie. Różnica między zasobami aplikacji i zasobami okna polega na tym, że zasoby aplikacji mogą być gromadzone za pomocą dowolnego obiektu DependencyObject w aplikacji, również tych zdefiniowanych w innych złożeniach. Użyjesz go, gdy chcesz tego osiągnąć :-)

u.

+0

Istnieje specjalna obsługa w WPF dla stylów, których typ docelowy nie pochodzi z formantów, takich jak TextBlock. Jak wyjaśnia Racheal, styl TextBlock jest obsługiwany inaczej po umieszczeniu w Application.Resources versus Window. – CodeNaked

Powiązane problemy