Laden...

WPF: Organisation von verschiedenen Themes für Custom Controls

Erstellt von p!lle vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.682 Views
p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren
WPF: Organisation von verschiedenen Themes für Custom Controls

Moin,

ich bin grade am Verzweifeln, was das Thema 'Themes' angeht.
Der Beitrag Aktuelle Assembly einer XAML-Datei herausfinden gehört im Groben auch mit zum Thema, dies war ein erster Versuch um das Problem in den Griff zu bekommen.

Worum geht es?
Das Programm soll verschiedene Themes unterstützen (aktuell 2, später evtl. noch mehr). Themes nicht im Sinne von Luna, Aero o.ä., sondern eigene Themes.
Für Farben etc. ist auch soweit alles klar, dort wird einfach das entsprechende ResourceDictionary für die Applikation getauscht, das funktioniert soweit.

Nun ist es allerdings so, dass die Custom Controls je nach Theme einen komplett anderen Aufbau haben. Gibt es nur ein Theme, wird über die Generic.xaml, das ThemeInfo-Attribute für die Assembly und die DefaultStyleKeyProperty alles geregelt.

Und an der Stelle weiß ich nun nicht weiter. Ich würde gerne in der Generic.xaml je nach gewählten Theme andere Einträge aufnehmen, woran ich bisher aber gescheitert bin.

Ist es überhaupt möglich, so etwas zu realisieren?
Wie realisiert ihr das in euren Projekten?

Über einen Gedankenanstoß würde ich mich freuen. 🙂

P
157 Beiträge seit 2014
vor 8 Jahren

Hallo,

theming ist ein recht komplexes thema, das du nicht so einfach lösen kannst. du wirst eine klasse benötigen, die deine themenauswahl entsprechend in einen dateinamen umwandelt und das xaml/baml lädt. der schlüssel zum ganzen dabei sind die richtigen x:key-werte für die styles...das ist ein ganz schönes gewusel.

ich würde dir raten, um dich in das thema einzuarbeiten, ein einfaches projekt zu suchen, dafür gibts sicherlich viele beispiele. versuch dabei solche zu finden die keine hacks verwenden...davon gibts auf codeplex und co genug...

Wenn's zum weinen nicht reicht, lach drüber!

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Danke für deine Antwort, ich denke aber dass sie mir nicht weiterhilft. 😉

Zum Thema 'Themes' an sich findet man viel, ja. Das habe ich mir auch schon alles reingezogen.
Nur zum Thema 'Themes und Custom Controls' findet man eben nicht sehr viel bis gar nichts.

P
157 Beiträge seit 2014
vor 8 Jahren

Auf der Seite wpftutorials.net findest du ansätze dafür, falls du das noch nicht kennst, dort stehen viele grundlagen fürs wpf drin.

das thema ist wie gesagt sehr komplex, ich hab auch ne weile gebraucht und ne anleitung von a-z wirst du wahrscheinlich nicht finden, sonst hätte ich dir nen link gegeben.

Wenn's zum weinen nicht reicht, lach drüber!

301 Beiträge seit 2009
vor 8 Jahren

Ich mache es immer so, dass ich meine Controls in einer Assembly halte mit einem "DefaultTheme" über die Generic.xaml

Für jedes weitere andere Theming dieser Controls mache ich eine neue Assembly die darauf referenzieren und neue Resourcedictionaries definieren.

Wenn ich das Theme jetzt wechseln will Cleare ich mein Application Resourcedictionary und füge das neue Basisresourcedictionary aus der Theme dll hinzu.

Sonderfälle gibt es hier allerdings auch z.B. beim Themen von der Window Komponente.

Ich bin nicht sicher ob ich dich richtig verstanden habe bzw. worin genau dein Problem liegt aber vielleicht hilft dir das ja schon mal.

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Oh man, anscheinend bin ich durch die aktuelle Struktur des Projektes auf einem ganz schönen Holzweg gewesen.

Es ist natürlich auch für Custom Controls ausreichend, wenn der entsprechende Style in den Ressourcen der Applikation getauscht wird - keine Ahnung warum ich die ganze Zeit die Generic.xaml anpassen wollte... 🤔

Danke, es hat mir jetzt erstmal weitergeholfen. 👍

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Ich habe nochmal ein bissel weiter gespielt und mir stellt sich jetzt noch eine Frage.

Dazu einmal der Aufbau meines Testprojektes:

MyCustomControl.cs

using System.Windows;
using System.Windows.Controls;

namespace ThemeTest.Views
{
    public class MyCustomControl : Button
    {
        #region Constructors

        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        #endregion
    }
}

MyCustomControl.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:views="clr-namespace:ThemeTest.Views">

    <Style TargetType="{x:Type views:MyCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Blue" BorderThickness="2">
                        <Border BorderBrush="White" BorderThickness="2">
                            <Border BorderBrush="Red" BorderThickness="2" />
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

MyUserControl.xaml

<views:MyCustomControl x:Class="ThemeTest.Views.MyUserControl"
                       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                       xmlns:views="clr-namespace:ThemeTest.Views">

</views:MyCustomControl>

MainWindow.xaml

<Window x:Class="ThemeTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:ThemeTest.Views"
        Title="MainWindow" Width="800" Height="600" WindowStartupLocation="CenterScreen">
    <Grid>
        ...
                <views:MyCustomControl Width="200" Height="400" Grid.Row="1" />
        ...
                <views:MyUserControl Width="200" Height="400" Grid.Row="1" />
        ...
    </Grid>
</Window>

In der App.xaml binde ich nun den Style für MyCustomControl ein:

<ResourceDictionary Source="pack://application:,,,/ThemeTest;component/Views/Template/MyCustomControl.xaml" />

Das sichtbare Ergebnis findet ihr im Dateianhang.

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Binde ich den Style NICHT in der App.xaml, sondern in der Generic.xaml ein, sieht das Ergebnis so aus.

Es erschließt sich mir nicht, warum das UserControl den Style einmal zieht und einmal nicht. Zumal er ja zuerst in der Application nach dieser Resource suchen sollte...
Jemand eine Idee? 🤔

301 Beiträge seit 2009
vor 8 Jahren
<[B]views:MyCustomControl[/B] x:Class="ThemeTest.Views.MyUserControl"  
                       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
                       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
                       xmlns:views="clr-namespace:ThemeTest.Views">  
  
</views:MyCustomControl>  

Vertan ?

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Nein, gewollt. 🙂

301 Beiträge seit 2009
vor 8 Jahren

Mir erschließt sich nicht ganz der Sinn. Was versuchst du mit dieser seltsamen Kombination zu erreichen? Ich würde erwarten, dass dein UserControl ein CustomControl enthält aber nicht selbst durch dieses definiert wird.

<views:MyUserControl x:Class="ThemeTest.Views.MyUserControl"
                       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                       xmlns:views="clr-namespace:ThemeTest.Views">

        <views:MyCustomControl />

</views:MyUserControl>
P
157 Beiträge seit 2014
vor 8 Jahren

Hast du das in deiner AssemblyInfo.cs drin ?

[assembly:ThemeInfo(
    ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
                             //(wird verwendet, wenn eine Ressource auf der Seite 
                             // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
    ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
                                      //(wird verwendet, wenn eine Ressource auf der Seite, in der Anwendung oder einem 
                                      // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
)]

Wenn's zum weinen nicht reicht, lach drüber!

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

@KroaX:
Ich kann dir keinen genauen Grund dafür nennen. 😉
Es ist ja durchaus technisch möglich und ich möchte einfach gerne verstehen, warum das Style-Verhalten je nach Eintrag in der App.xaml/Generic.xaml anders ist.

@Parso:
Ja. Sonst würde die Generic.xaml ja nicht funktionieren. 😉

301 Beiträge seit 2009
vor 8 Jahren

Ich glaube wenn es in der Generic.xaml drin steht wird im jeweiligen CustomControl auch "OnApplyTemplate()" aufgerufen. Wenn das der Fall ist wird das Control korrekt eingebunden.

Wenn dies nicht der Fall ist wird es auch oft nicht korrekt eingebunden. Es gibt dafür ein paar Spezialfälle wie ich bereits erwähnte ( z.B. das Window ). Ob der Button auch dazu gehört weiß ich grade nicht aber wäre durchaus möglich.

Spezialfall deshalb weil normalerweise die Generic.xaml nur dann automatisch erkannt wird, wenn das CustomControl in derselben Assembly steckt wie die jeweilge Generic.xaml.

Das ist nen ziemlich undurchsichtiges Thema und mir fällt es grade selbst schwer das in Worte zu fassen.

Du kannst mal probieren in den Ctor deines CustomControls folgendes einzubetten :


 public MyCustomControl()
        {
            SetResourceReference(StyleProperty, typeof (MyCustomControl));
        }

Ich hab mir häufig damit geholfen zu schauen ob das OnApplyTemplate aufgerufen wird oder nicht. Wenn nein dann ist meist nochwas im argen.

Ansonsten bleib ich dabei das dein Beispiel leider auch echt überhaupt nicht Praxistauglich ist auch wenn ich durchaus verstehe das du die Details erkennen möchtest.

Hinweis: Du kannst dir vermutlich recht viel in der MahApps Library abschauen was Styling angeht.

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 8 Jahren

Ich denke ich habe das Problem gefunden - das UserControl habe ich mal aus meinen Überlegungen verbannt, denke aber dort hat das Problem die gleiche Ursache. 😉

Der 'DefaultStyleKeyProperty'-Eintrag hat nur eine Auswirkung, wenn der Default-Style für das CC über die Generic.xaml gesetzt wird (aus meiner Sicht kann man nirgendwo anders den Default-Style definieren).
Trägt man den Style in der App.xaml ein, ist dies nicht der Default-Style.

Wir nutzen in unseren Projekten oft sowas:

<Style x:Key="FrameworkStyle" TargetType="{x:Type FrameworkElement}">
    <Setter Property="Height" Value="300" />
    <Setter Property="Width" Value="100" />
</Style>

Wenn der 'DefaultStyleKeyProperty'-Eintrag vorhanden und der Default-Style für das CC in der Generic.xaml eingetragen ist, kann nun mit

<views:MyCustomControl Style="{StaticResource FrameworkStyle}" />

der Default-Style erweitert werden.

Ist der Style für das CC stattdessen in der App.xaml eingetragen, funktioniert das so nicht, dann wird der Style quasi überschrieben.

Auf jeden Fall stehe ich jetzt wieder am Anfang mit meinen Überlegungen. 😁