Resources + Systemfarben

    • WPF

      Resources + Systemfarben

      Resourcen-Prinzipien
      In Wpf kann man auf allen Ebenen (Scopes) ResourceDictionaries anlegen, und da je beliebige Objekte reinwerfen.
      Um die Objekte wiederzufinden ist natürlich sowas wie ein Schlüssel erforderlich.
      Für beliebige Objekte ist ein x:Key [Schlüssel] anzugeben, und der Schlüssel kann ebenfalls ganz beliebig sein.
      Derselbe Schlüssel darf auf verschiedenen Ebenen verwendet werden - Auflösungs-Regel ist: Ein Element beginnt die Resourcen-Suche anhand eines Keys im lokalen Scope, und steigt immer höher bis auf Application-Ebene. Jedoch bei der ersten Key-Übereinstimmung ist die Suche beendet, und die zum Key gehörige Resource wird genommen.
      Dieses Prinzip heisst übrigens "Abschattierung", und kommt auch in anneren Bereichen der Programmierung so ähnlich vor - ja sogar in der realen Welt - (etwa wenn Verträge gesetzliche Default-Bestimmungen re-definieren - Kündigungsfristen etc).

      Ein weiteres Konzept ist: Manche Klassen haben auch eine eingebauten Schlüssel-Property, etwa Style.TargetType oder DataTemplate.DataType oder ControlTemplate.TargetType etc - man sieht: als eingebauter Schlüssel wird gern ein System.Type verwendet. Das ist dann Bestandteil eines Mechanismus der "Selbstanwendung", über den der Style oder das Template (oder sonstwas) per default sich selbst auf Objekte des angegebenen Typs anwendet, solange das Objekt im Scope des ResourceDictionaries liegt, und im Xaml nix anderes angegeben ist.

      Scope-Demo
      Um obiges zu demonstrieren habich klein Beispiel gebastelt - in 4 tw. verschachtelten DockPanels liegen 6 numerierte Label mit "gestylten" Rahmen-Dicke und Rahmen-/Text-Farben:

      Xaml dazu:

      XML-Quellcode

      1. <Window x:Class="ScopeDemo"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. Title="ScopeDemo" Height="400" Width="300" Left="0" Top="0" Background="#FFF7F766">
      5. <FrameworkElement.Resources>
      6. <Style TargetType="{x:Type Label}"> <!--outerStyle-->
      7. <Setter Property="BorderBrush" Value="Black"/>
      8. <Setter Property="BorderThickness" Value="13"/>
      9. <Setter Property="Margin" Value="10"/>
      10. <Setter Property="Padding" Value="15"/>
      11. <Setter Property="FontSize" Value="15"/>
      12. <Setter Property="FontWeight" Value="Bold"/>
      13. <Setter Property="Foreground" Value="#FFF71010"/>
      14. </Style>
      15. <Style x:Key="keyedStyle" BasedOn="{StaticResource {x:Type Label}}" TargetType="Label"> <!--keyedStyle-->
      16. <Setter Property="BorderThickness" Value="5"/>
      17. </Style>
      18. </FrameworkElement.Resources>
      19. <DockPanel>
      20. <Label DockPanel.Dock="Top">1</Label>
      21. <DockPanel>
      22. <Label>2</Label>
      23. <DockPanel>
      24. <FrameworkElement.Resources>
      25. <Style BasedOn="{StaticResource {x:Type Label}}" TargetType="Label"> <!--middleStyle-->
      26. <Setter Property="BorderBrush" Value="Blue"/>
      27. </Style>
      28. </FrameworkElement.Resources>
      29. <Label DockPanel.Dock="Top">3</Label>
      30. <Label>4</Label>
      31. <DockPanel>
      32. <FrameworkElement.Resources>
      33. <Style BasedOn="{StaticResource {x:Type Label}}" TargetType="Label"> <!--innerStyle-->
      34. <Setter Property="Foreground" Value="Blue"/>
      35. </Style>
      36. </FrameworkElement.Resources>
      37. <Label DockPanel.Dock="Top">5</Label>
      38. <Label Style="{StaticResource keyedStyle}">6</Label>
      39. </DockPanel>
      40. </DockPanel>
      41. </DockPanel>
      42. </DockPanel>
      43. </Window>
      Hier kommen 4 Styles zum Einsatz - letztendlich alle basierend auf dem ersten, "outerStyle", angelegt inne Window-Resourcen - hier der oberste Scope. Das ist gleich so einer ohne explizitem Key - sodass sein TargetType (Label) auch sein Key ist, und ausserdem alle Labels in seim Scope defaultmässig diesen Style auf sich anwenden.
      Auf diesem "outerStyle" basiert ein Keyed Style (Zeile #15), mit modifizierter BorderThickness. Beachte, wie die BasedOn-Property den "outerStyle" referenziert: Als Referenz-Key ist der Datentyp Label angegeben.
      Hingegen der eigene, explizite x:Key des "keyedStyle" ist einfach Text: "keyedStyle", also ein String.
      Da er keyed ist, kommt dieser Style nur zur Anwendung, wenn ausdrücklich auf ihn verwiesen wird - wie in Zeile #38.
      Im zweiten eingeschachtelten DockPanel findet sich der "middleStyle", auch basierend auf "outerStyle" - BorderBrush-modifiziert - #25.
      Also Label 1 + 2 sind schwarzgerahmt und rot betextet, weil sie "outerStyle" anwenden.
      Label 3 + 4 sind blaugerahmt, weil sie "middleStyle" anwenden.
      Im innersten DockPanel ist noch ein Style - "innerStyle", der denselben BasedOn-Key verwendet wie die vorherigen beiden Styles. Aber dieser addressiert nun den "middleStyle", welcher "outerStyle" an dieser Stelle ja abschattiert.
      Daher "erbt" Label 5 den blauen Rahmen, wenns "innerStyle" auf sich anwendet.
      Label 6 hingegen hat sich explizit einen keyedStyle zugewiesen, und erhält dadurch den schwarzen Rahmen und die rote Schrift des "outerStyle" - allerdings mit des "keyedStyle"s modifizierter Rahmenbreite.

      Die SystemColors-Klasse

      So - also das mitte Scopes, Explizit-/ Auto-Keys, und die Style-Selbstanwendung bei Auto-Keys muss verstanden sein fürs Folgende.
      Ich hab mich heuer mit der statischen Klasse SystemColors beschäftigt, darin sind 32 verschiedene Farben definiert, und ihre "Farb-Zwecke" - etwa "Highlight..." - hab ich herausgefunden - wird gern als Hintergrundfarbe von Selektierung genommen - sei es in List- oder Text-boxen or whatever.
      Wie gesagt: von derlei Farb-Zwecken gibts noch 31 mehr, etwa
      "HighlightText": Selection-Schriftfarbe | "Control": Control-Hintergrund | "ControlText": C-Vordergrund | ...

      Zu jedem dieser Zwecke fahren inne SystemColors-Klasse 3 Objekte rum: eine Color, ein Brush, und ein Brushkey, also zum Highlighten gibts HighlightColor, HighlightBrush, HighlightBrushKey.

      Warum?
      Weil in Wpf auch die Systemfarben einstellbar sind.
      Also ein gehighlightetes Visual holt mittm HighlightBrushKey vom Resourcen-Management seinen Brush. Wenn nix anneres festgelegt ist, wird es SystemColors.HighlightBrush erhalten, aus einem globalen ResourceDictionary als letzte Instanz.
      Wenn nichts anneres festgelegt ist.
      Ich kann ja auch ein eigenes ResourceDictionary anlegen, und da kann ich auch einen Brush reinlegen, und dem kann ich denselben Schlüssel geben:

      XML-Quellcode

      1. <ResourceDictionary>
      2. <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
      3. </ResourceDictionary>

      Ätsch! - wenn nun ein Visual im Scope meines ResourceDictionaries liegt, dann wird es für denselben Key meinen Brush erhalten - nicht mehr den des Systems.

      Man kann also mit wenigen Zeilen ganze Farb-Themes redefinieren, wenn man das toll findet. Anwendungsweit, aber auch jeder engere Scope: einzelne Fenster, Panels, wie wolle.
      Oder man bastelt einen Style speziell für Listboxen:

      XML-Quellcode

      1. <Style TargetType="ListBox">
      2. <Style.Resources>
      3. <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Khaki"/>
      4. <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="{x:Static SystemColors.ControlTextColor}"/>
      5. </Style.Resources>
      6. </Style>

      So - meine Listboxen highlighten jetzt in Khaki, unter Beibehaltung der Textfarbe. Findich viel angenehmer als das Standard-Highlighting mit weissem Text auf DunkelBlau.
      Ich hab diesen Style per Scope Super-Hoch aufgehängt - höher noch als Application-Ebene.
      Nämlich in meine Wpf-Helpers hab ich ihn in ein ResourceDichtionary gemacht, und alle meine Wpf-Anwendungen inkludieren dieses, zB so:

      XML-Quellcode

      1. <Application x:Class="Application"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      5. mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      6. xmlns:sys="clr-namespace:System;assembly=mscorlib"
      7. xmlns:vm="clr-namespace:_WpfTemplate.Viewmodel"
      8. xmlns:vw="clr-namespace:_WpfTemplate.View"
      9. StartupUri="View/MainWindow.xaml">
      10. <Application.Resources>
      11. <ResourceDictionary>
      12. <ResourceDictionary.MergedDictionaries>
      13. <ResourceDictionary Source="pack://application:,,,/WpfHelpers;component/Resources/GeneralStyles.xaml"/>
      14. <!--weitere Resource-Files... -->
      15. </ResourceDictionary.MergedDictionaries>
      16. </ResourceDictionary>
      17. <!--weitere einzelne Resources... -->
      18. </Application.Resources>
      19. </Application>

      Zeile #13 ist die Include-Anweisung - die Pfad-Angabe ist ein bischen krank - das ist ein Uri. Was das pack, das application, die 3 ,, das ;component da zu bedeuten hat hab ich noch nicht genau begriffen, man kann ja nach "Wpf Uri pack" googeln, da kommen iwelche genauere Erklärungen.
      Mir reicht erstmal zu wissen, dass WpfHelpers meine eingebundene Assymbly ist, und /Resources/GeneralStyles.xaml ist der Pfad von deren Projekt-Verzeichnis zu meim GeneralStyles-Dictionary-File.

      Sample-Application
      Inne Sample-Application hab ich ausserdem noch was anneres eingebunden, nämlich ein Resource-Dictionary, mit dem man jeden einzelnen FarbZweck auf "Red" umstellen kann. Und dazu habich eine Listbox gebastelt, um die ResourceKeys auszuwählen. Und ein Test-Control, wo man dann sieht, welcher Teil der Controls bei welchem ResourceKey rot wird.

      Das Bildle zeigt, wie der HighlightBrushKey des selektierten ListboxItems Hintergrund errötet.
      Die Sample-App enthält auch meine Helpers - also nicht erschrecken über die grosse Code-Menge - die eigentliche Haupt-Assembly ist sehr überschaubar.
      Dateien

      Dieser Beitrag wurde bereits 9 mal editiert, zuletzt von „ErfinderDesRades“ ()