myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » GUI: WPF und XAML » Style überschreibt eigene Control-Klasse?
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Style überschreibt eigene Control-Klasse?

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013


GeneVorph ist offline

Style überschreibt eigene Control-Klasse?

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

ich versuche derzeit meine ersten Gehversuche in WPF etwas aufzufrischen und eigene Styles anzuwenden. Dabei bin ich auf ein Problem gestoßen, bei dem ich nicht weiterkomme.

Ausgangspunkt:
für meine App habe ich drei ToggleButtons, von denen aber jeweils nur einer aktiv sein können soll. Das Verhalten ist also eher das von RadioButtons. Kurz: da ich das Verhalten von RadioButtons mit der Funktionalität und Optik von ToggleButtons brauche, habe ich eine Klasse RadioToggleButton erstellt, die von RadioButton erbt. Sieht dann so aus:

C#-Code:
public class RadioToggleButton : RadioButton
    {
        protected override void OnToggle()
        {
            if (IsChecked == true) IsChecked = IsThreeState ? (bool?)null : (bool?)false;
            else IsChecked = IsChecked.HasValue;
        }
    }

Die drei RadioToggleButtons habe ich in XAML folgendermaßen erstellt:

XML-Code:
<Grid DockPanel.Dock="Left" Background="Gray" Width="80">
            <StackPanel>
                <local:RadioToggleButton x:Name="tglButton1" Background="Gray" Height="80" Content="Option 1" Click="tglButton1_Click" Style="{StaticResource {x:Type ToggleButton}" />
                <local:RadioToggleButton x:Name="tglButton2" Background="Gray" Height="80" Content="Option 2" Click="tglButton2_Click" Style="{StaticResource {x:Type ToggleButton}}" />
                <local:RadioToggleButton x:Name="tglButton3" Background="Gray" Height="80" Content="Option 3" Click="tglButton3_Click" Style="{StaticResource {x:Type ToggleButton}}" />
            </StackPanel>
        </Grid>

Das Ergebnis sieht dann aus wie in Bild 1.

Nun habe ich versucht etwas mit Styles herumzuspielen, und zwar folgendermaßen:

XML-Code:
<Window.Resources>
        <Style x:Key="TestStyle" TargetType="local:RadioToggleButton">
            <Setter Property="Background" Value="Red"/>
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Background" Value="Green"/>

                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

Wenn ich nun aber den Style auf meine RadioToggleButtons anwenden möchte...

XML-Code:
<StackPanel>
                <local:RadioToggleButton x:Name="tglButton1" Background="Gray" Height="80" Content="Option 1" Click="tglButton1_Click" Style="{StaticResource TestStyle" /> <!-- ... -->

Dann bekommen meine RadioToggleButtons zusätzlich normale RadioButton-Felder (s. Bild 2). Das war natürlich nicht meine Absicht.

Habe ich lediglich was falsch implementiert oder liegt ein Denkfehler anderer Art vor?

Dank und Gruß
Vorph

GeneVorph hat dieses Bild (verkleinerte Version) angehängt:
Bild 1.png
Volle Bildgröße

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von GeneVorph am 06.05.2019 20:18.

Neuer Beitrag 06.05.2019 20:17 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

sorry - hier ist Bild Nr 2

GeneVorph hat dieses Bild (verkleinerte Version) angehängt:
Bild 2.png
Volle Bildgröße

Neuer Beitrag 06.05.2019 20:19 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
trashkid2000 trashkid2000 ist männlich
myCSharp.de-Mitglied

Dabei seit: 27.12.2010
Beiträge: 156


trashkid2000 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hi,

es reicht nicht, einfach nur von RadioButton zu erben und in der vererbten Klasse Methoden zu überschreiben. Denn so änderst Du nur das Verhalten.

Was Du machen möchtest, ist, dass Du das Aussehen verändern willst (ja, und das Verhalten).

Da kommst Du nicht drum rum, das ControlTemplate zu überschreiben.
Eine gute Übersicht findest Du hier:  RadioButton Styles und Templates

lG, Marko

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von trashkid2000 am 06.05.2019 22:50.

Neuer Beitrag 06.05.2019 22:49 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
witte
myCSharp.de-Mitglied

Dabei seit: 03.09.2010
Beiträge: 830


witte ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Es wäre besser MVVM zu verwenden: Dann kann man bei den ToggleButtons bleiben und setzt im ViewModel die jeweils anderen beiden auf false wenn einer geklickt wird.
Was soll das überhaupt werden? Baust du ein TabControl nach?
Neuer Beitrag 07.05.2019 12:39 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@trashkid2000: Danke für den Link und den Hinweis mit dem ControlTemplate. Jetzt weiß ich zumindest, in welche Richtung ich dabei gehen muss.

@witte: Danke auch für deinen Hinweis. Möglicherweise versuche ich diese Variante - es hätte den Vorteil keinen eigenen Button entwerfen zu müssen.

Zitat:
Was soll das überhaupt werden?

Ehrlich gesagt eine Fingerübung. Ich code rein zum Spaß (wenn ich alle paar Monate mal wirklich dazu komme). Ich hatte die Idee, so ein Side-Menu zu machen, bei dem sich - wenn man ein Menupunkt ausgewählt hat - rechts davon ein Panel öffnet mit den Submenüpunkten (Im Bild 2 siehst du das Panel, weil ich zufällig den Button auch geclickt hatte).
Neuer Beitrag 07.05.2019 19:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
witte
myCSharp.de-Mitglied

Dabei seit: 03.09.2010
Beiträge: 830


witte ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Dann kannst du doch einfach ein TabControl verwenden, mit den  Tabs an der Seite. Und danach TabItem.Header stylen.
Neuer Beitrag 07.05.2019 20:55 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Dann kannst du doch einfach ein TabControl verwenden

großes Grinsen Danke - das war mein erster Versuch. Dann dachte ich:"let's go sophisticated" - einfach um mal zu schauen, wie ich die Idee noch umsetzen kann.

Übrigens: ich hab's jetzt so gemacht, dass in meinem ViewModel die Logic das Verhalten der Buttons steuert (wird ein Button geklickt, werden die anderen deaktiviert). Obwohl ich mir nicht 100% sicher bin, ob das Steuern einer Logik im ViewModel, die die Optik beeinflusst, nicht ein Bruch der Trennung von Design und Busisnesslogik ist. Weil ja der Code quasi Styleaspekte festlegt ... streng genommen ... vielleicht hab ich jetzt auch nur n Knoten im Hirn verwirrt
Neuer Beitrag 08.05.2019 19:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
MrSparkle MrSparkle ist männlich
myCSharp.de-Team

avatar-2159.gif


Dabei seit: 16.05.2006
Beiträge: 5.255
Herkunft: Leipzig


MrSparkle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ist schon richtig so. Das ViewModel repräsentiert auch die Logik der Benutzeroberfläche. Schau mal in  [Artikel] MVVM und DataBinding, da gibt es auch noch ein paar andere Beispiele dazu.
Neuer Beitrag 08.05.2019 20:35 Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

OK, nach einem ersten Erfolg, nun Ernüchterung.

Kurz nochmal zur angestrebten Funktionalität: Ich klicke "Option 1" und ein Panel gleitet rechts der Buttons auf (zu sehen in Bild 2). Noch ein Klick auf "Option 1), und das Panel gleitet wieder zu. Klicke ich zuerst Option 1 und dann Option 2, sollte das Panel idealerweise nochmals aufgleiten (sieht besser aus, als wenn es direkt geöffnet bleibt...IMHO).

Ich habe den Ansatz mit den ToggleButtons nochmals aufgegriffen und bin zu folgendem Ergebnis gekommen:
- ich kann nun Option 1 klicken und das Panel gleitet auf. Bei nochmaligem Klicken schließt es.
- ich kann Option 1 und dann Option 2 klicken. Das Panel öffnet sich, gleichzeitig bleibt aber natürlich der Checked-State von Option 1 erhalten. Das sollte so eigentlich nicht sein. Wenn ich progammatisch den State auf "Unchecked" ändere (also per Code), dann schließt das Panel natürlich und Option zwei verharrt ohne Funktion im State "Checked"

Bevor ich euch meinen COde zeige: zwei Ideen hatte ich noch:
a) ich steige auf einfache Buttons um, und versuche den Style (Background) des Buttons über einen Bool zu steuern, der dafür sorgt, dass die Optik stimmt.
b doch nochmal auf die RAdioButtons zurückkommen. )trashkid2000 schrieb: das ControlTemplate überschreiben. Das entpuppte sich aber als zu schwierig für mich (ich hab's nicht hinbekommen, dass meine Radiobuttons am Schluss wie die normalen Buttons aussahen^^)

Hier mein XAML-Code (dafür habe ich den Code im ViewModel auskommentiert, da nun der XAML-Code Visibility/Collapsed für das Panel übernimmt):

XML-Code:
<StackPanel>
                <ToggleButton x:Name="tglButton1" Content="Option 1" Height="80" Click="tglButton1_Click">
                    <ToggleButton.Triggers>
                        <EventTrigger RoutedEvent="ToggleButton.Checked">
                            <BeginStoryboard>
                                <Storyboard x:Name="HidePanel">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="0" To="80" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="ToggleButton.Unchecked">
                            <BeginStoryboard>
                                <Storyboard x:Name="ShowPanel">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="80" To="0" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ToggleButton.Triggers>
                </ToggleButton>

<!-- ab hier wiederholt es sich im Prinzip für die ToggleButtons 2 und 3 -->

<ToggleButton x:Name="tglButton2" Content="Option 2" Height="80" Click="tglButton2_Click">
                    <ToggleButton.Triggers>
                        <EventTrigger RoutedEvent="ToggleButton.Checked">
                            <BeginStoryboard>
                                <Storyboard x:Name="Hide">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="0" To="80" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="ToggleButton.Unchecked">
                            <BeginStoryboard>
                                <Storyboard x:Name="Show">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="80" To="0" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ToggleButton.Triggers>
                </ToggleButton>
                <ToggleButton x:Name="tglButton3" Content="Option 3" Height="80" Click="tglButton3_Click">
                    <ToggleButton.Triggers>
                        <EventTrigger RoutedEvent="ToggleButton.Checked">
                            <BeginStoryboard>
                                <Storyboard x:Name="Hide2">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="0" To="80" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="ToggleButton.Unchecked">
                            <BeginStoryboard>
                                <Storyboard x:Name="Show2">
                                    <DoubleAnimation Storyboard.TargetName="ToggleGrid" Storyboard.TargetProperty="Width" From="80" To="0" Duration="0:0:0.3">
                                        <DoubleAnimation.EasingFunction>
                                            <PowerEase EasingMode="EaseIn"></PowerEase>
                                        </DoubleAnimation.EasingFunction>
                                    </DoubleAnimation>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ToggleButton.Triggers>
                </ToggleButton>
            </StackPanel>

Noch eine letzte Kleinigkeit: ich habe festgestellt, dass ich nun beim Start der App mit ToggleGrid.Visible beginnen muss. Sonst funktioniert es nicht (als hätten die Button-Klicks keine Funktion?). Woran könnte das liegen?

Danke und Gruß
Vorph
Neuer Beitrag 09.05.2019 21:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@MrSparkle Danke für die Links! Ich merke, da gibt es noch Wissenslücken zu füllen.
Neuer Beitrag 09.05.2019 21:41 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
GeneVorph GeneVorph ist männlich
myCSharp.de-Mitglied

Dabei seit: 09.02.2015
Beiträge: 90
Entwicklungsumgebung: Visual Studio 2013

Themenstarter Thema begonnen von GeneVorph

GeneVorph ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Nach vielen Stunden Recherche zum Thema und Einarbeit ins Thema, hier meine Lösung - falls mal jemand vor dem gleichen Problem steht:

Gewünschtes Verhalten: Im Prinzip möchte ich zwei bis drei ToggleButtons, die sich wie RadioButtons verhalten - immer nur einer kann zu einem Zeitpunkt IsChecked sein.

Die Lösung sieht dann so aus, dass man 2 Styles erstellt: ein Basis-Style für RadioButtons und einen Style für die ToggleButtons. Aber Code sagt ja bekanntlich mehr als tausend Worte:

XML-Code:
<!-- hier der Basis-Style für die ToggleButtons-->
<Style x:Key="TGLTemplate" TargetType="ToggleButton">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ToggleButton">
                            <Border BorderBrush="{TemplateBinding BorderBrush}"
                                Background="{TemplateBinding Background}"
>
                                <ContentPresenter HorizontalAlignment="Center"
                                              VerticalAlignment="Center"
/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Background" Value="Blue"/>
                </Trigger>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Background" Value="Red" />
                    </Trigger>
                </Style.Triggers>
        </Style>

<!-- hier der Basis-Style für die RadioButtons-->
        <Style x:Key="MyTemplate" TargetType="RadioButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
<!-- hier der eigentliche "Trick: im ControlTemplate der RadioButtons befindet sich ein ToggleButton, dessen Style auf den ToggleButton-Style referenziert-->
                        <ToggleButton Style="{StaticResource TGLTemplate}" IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                          Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Wenn man das jetzt noch sauber in ein ResourceDictionary auslagert und ein bisschen aufräumt, ist das hübsch MVVM verträglich. Und war so einfach^^

Gruß
Vorph
Neuer Beitrag 11.05.2019 21:11 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 7 Monate.
Der letzte Beitrag ist älter als 7 Monate.
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 13.12.2019 04:17