Donner aux applications WPF un aspect de style Metro, même sous Windows 7? (Fenêtre Chrome / Thème / Thème)

123

J'aime le chrome de la fenêtre sur la nouvelle suite Office et Visual Studio:

entrez la description de l'image ici

Je suis encore en train de développer des applications pour Windows 7 bien sûr, mais je me demande s'il existe un moyen rapide et facile (lire: style WPF ou bibliothèque Windows) pour émuler ce style. J'ai fait du style de chrome de fenêtre dans le passé, mais il est vraiment difficile de lui donner un aspect et un comportement parfait.

Quelqu'un sait-il s'il existe des modèles ou des bibliothèques existants pour ajouter une apparence et une convivialité «Interface utilisateur moderne» à mes applications WPF?

Daniel
la source
8
Ce package Guide / NuGet peut être utile: MahaApps Metro Il contient un ensemble de styles et de contrôles pour créer des applications WPF avec Metro Look and feel.
Oliver Vogel
Les questions nous demandant de recommander ou de trouver un livre, un outil, une bibliothèque de logiciels, un didacticiel ou toute autre ressource hors site sont hors sujet pour Stack Overflow car elles ont tendance à attirer des réponses avisées et du spam. Décrivez plutôt le problème et ce qui a été fait jusqu'à présent pour le résoudre.
Scott Solmer

Réponses:

149

Ce que j'ai fait, c'est créer ma propre fenêtre et mon propre style. Parce que j'aime avoir le contrôle sur tout et que je ne voulais pas que certaines bibliothèques externes utilisent simplement une fenêtre. J'ai regardé MahApps.Metro déjà mentionné sur GitHub

MahApps

et aussi une très belle interface utilisateur moderne sur GitHub . (.NET4.5 uniquement)

Interface utilisateur moderne

Il y en a un de plus, c'est Elysium mais je n'ai vraiment pas essayé celui-ci.

Élysée

Le style que j'ai fait était vraiment facile quand j'ai regardé comment c'était fait. Maintenant, j'ai ma propre fenêtre et je peux faire tout ce que je veux avec xaml ... pour moi, c'est la principale raison pour laquelle j'ai fait la mienne. Et j'en ai fait un de plus pour vous aussi :) Je devrais probablement dire que je ne pourrais pas le faire sans explorer Modern UI, c'était d'une grande aide. J'ai essayé de le faire ressembler à VS2012 Window. Ça ressemble à ça.

Ma fenêtre

Voici le code (veuillez noter qu'il cible .NET4.5)

public class MyWindow : Window
{

    public MyWindow()
    {
        this.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, this.OnCloseWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, this.OnMaximizeWindow, this.OnCanResizeWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, this.OnMinimizeWindow, this.OnCanMinimizeWindow));
        this.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, this.OnRestoreWindow, this.OnCanResizeWindow));
    }

    private void OnCanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip;
    }

    private void OnCanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = this.ResizeMode != ResizeMode.NoResize;
    }

    private void OnCloseWindow(object target, ExecutedRoutedEventArgs e)
    {
        SystemCommands.CloseWindow(this);
    }

    private void OnMaximizeWindow(object target, ExecutedRoutedEventArgs e)
    {
        SystemCommands.MaximizeWindow(this);
    }

    private void OnMinimizeWindow(object target, ExecutedRoutedEventArgs e)
    {
        SystemCommands.MinimizeWindow(this);
    }

    private void OnRestoreWindow(object target, ExecutedRoutedEventArgs e)
    {
        SystemCommands.RestoreWindow(this);
    }
}

Et voici les ressources:

<BooleanToVisibilityConverter x:Key="bool2VisibilityConverter" />

<Color x:Key="WindowBackgroundColor">#FF2D2D30</Color>
<Color x:Key="HighlightColor">#FF3F3F41</Color>
<Color x:Key="BlueColor">#FF007ACC</Color>
<Color x:Key="ForegroundColor">#FFF4F4F5</Color>

<SolidColorBrush x:Key="WindowBackgroundColorBrush" Color="{StaticResource WindowBackgroundColor}"/>
<SolidColorBrush x:Key="HighlightColorBrush" Color="{StaticResource HighlightColor}"/>
<SolidColorBrush x:Key="BlueColorBrush" Color="{StaticResource BlueColor}"/>
<SolidColorBrush x:Key="ForegroundColorBrush" Color="{StaticResource ForegroundColor}"/>

<Style x:Key="WindowButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="Foreground" Value="{DynamicResource ForegroundColorBrush}" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Padding" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid Background="{TemplateBinding Background}">
                    <ContentPresenter x:Name="contentPresenter"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                          Margin="{TemplateBinding Padding}"
                          RecognizesAccessKey="True" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="{StaticResource HighlightColorBrush}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" Value="{DynamicResource BlueColorBrush}" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter TargetName="contentPresenter" Property="Opacity" Value=".5" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="MyWindowStyle" TargetType="local:MyWindow">
    <Setter Property="Foreground" Value="{DynamicResource ForegroundColorBrush}" />
    <Setter Property="Background" Value="{DynamicResource WindowBackgroundBrush}"/>
    <Setter Property="ResizeMode" Value="CanResizeWithGrip" />
    <Setter Property="UseLayoutRounding" Value="True" />
    <Setter Property="TextOptions.TextFormattingMode" Value="Display" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyWindow">
                <Border x:Name="WindowBorder" Margin="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}}" Background="{StaticResource WindowBackgroundColorBrush}">
                    <Grid>
                        <Border BorderThickness="1">
                            <AdornerDecorator>
                                <Grid x:Name="LayoutRoot">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="25" />
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="15" />
                                    </Grid.RowDefinitions>
                                    <ContentPresenter Grid.Row="1" Grid.RowSpan="2" Margin="7"/>
                                    <Rectangle x:Name="HeaderBackground" Height="25" Fill="{DynamicResource WindowBackgroundColorBrush}" VerticalAlignment="Top" Grid.Row="0"/>
                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True" Grid.Row="0">
                                        <Button Command="{Binding Source={x:Static SystemCommands.MinimizeWindowCommand}}" ToolTip="minimize" Style="{StaticResource WindowButtonStyle}">
                                            <Button.Content>
                                                <Grid Width="30" Height="25" RenderTransform="1,0,0,1,0,1">
                                                    <Path Data="M0,6 L8,6 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                        Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2"  />
                                                </Grid>
                                            </Button.Content>
                                        </Button>
                                        <Grid Margin="1,0,1,0">
                                            <Button x:Name="Restore" Command="{Binding Source={x:Static SystemCommands.RestoreWindowCommand}}" ToolTip="restore" Visibility="Collapsed" Style="{StaticResource WindowButtonStyle}">
                                                <Button.Content>
                                                    <Grid Width="30" Height="25" UseLayoutRounding="True" RenderTransform="1,0,0,1,.5,.5">
                                                        <Path Data="M2,0 L8,0 L8,6 M0,3 L6,3 M0,2 L6,2 L6,8 L0,8 Z" Width="8" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                            Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1"  />
                                                    </Grid>
                                                </Button.Content>
                                            </Button>
                                            <Button x:Name="Maximize" Command="{Binding Source={x:Static SystemCommands.MaximizeWindowCommand}}" ToolTip="maximize" Style="{StaticResource WindowButtonStyle}">
                                                <Button.Content>
                                                    <Grid Width="31" Height="25">
                                                        <Path Data="M0,1 L9,1 L9,8 L0,8 Z" Width="9" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                            Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2"  />
                                                    </Grid>
                                                </Button.Content>
                                            </Button>
                                        </Grid>
                                        <Button Command="{Binding Source={x:Static SystemCommands.CloseWindowCommand}}" ToolTip="close"  Style="{StaticResource WindowButtonStyle}">
                                            <Button.Content>
                                                <Grid Width="30" Height="25" RenderTransform="1,0,0,1,0,1">
                                                    <Path Data="M0,0 L8,7 M8,0 L0,7 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
                                                        Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1.5"  />
                                                </Grid>
                                            </Button.Content>
                                        </Button>
                                    </StackPanel>
                                    <TextBlock x:Name="WindowTitleTextBlock" Grid.Row="0" Text="{TemplateBinding Title}" HorizontalAlignment="Left" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"  Margin="8 -1 0 0"  FontSize="16"  Foreground="{TemplateBinding Foreground}"/>
                                    <Grid Grid.Row="2">
                                        <Path x:Name="ResizeGrip" Visibility="Collapsed" Width="12" Height="12" Margin="1" HorizontalAlignment="Right"
                                        Stroke="{StaticResource BlueColorBrush}" StrokeThickness="1" Stretch="None" Data="F1 M1,10 L3,10 M5,10 L7,10 M9,10 L11,10 M2,9 L2,11 M6,9 L6,11 M10,9 L10,11 M5,6 L7,6 M9,6 L11,6 M6,5 L6,7 M10,5 L10,7 M9,2 L11,2 M10,1 L10,3" />
                                    </Grid>
                                </Grid>
                            </AdornerDecorator>
                        </Border>
                        <Border BorderBrush="{StaticResource BlueColorBrush}" BorderThickness="1" Visibility="{Binding IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource bool2VisibilityConverter}}" />
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="WindowState" Value="Maximized">
                        <Setter TargetName="Maximize" Property="Visibility" Value="Collapsed" />
                        <Setter TargetName="Restore" Property="Visibility" Value="Visible" />
                        <Setter TargetName="LayoutRoot" Property="Margin" Value="7" />
                    </Trigger>
                    <Trigger Property="WindowState" Value="Normal">
                        <Setter TargetName="Maximize" Property="Visibility" Value="Visible" />
                        <Setter TargetName="Restore" Property="Visibility" Value="Collapsed" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="ResizeMode" Value="CanResizeWithGrip" />
                            <Condition Property="WindowState" Value="Normal" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="ResizeGrip" Property="Visibility" Value="Visible" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="WindowChrome.WindowChrome">
        <Setter.Value>
            <WindowChrome CornerRadius="0" GlassFrameThickness="1" UseAeroCaptionButtons="False" />
        </Setter.Value>
    </Setter>
</Style>
Kapitán Mlíko
la source
1
Salut et merci beaucoup pour ce super code que vous avez publié. Juste une faveur à demander, est-il possible d'avoir une ombre sur la fenêtre? La seule chose que j'ai compris est en train de changer GlassFrameThicknessà 1. Mais l'ombre est trop forte et trop sombre. Comment puis-je changer son poids et son opacité?
xperator
Est-il très difficile de créer ma propre personnalisation des composants, au lieu d'utiliser MahApps?
Matheus Saraiva
Fantástico! Merci beaucoup pour cette excellente contribution, j'avais essayé de faire la même chose plusieurs fois mais je n'ai jamais obtenu un résultat aussi parfait.
Leodev
Supposons que je veuille augmenter l'épaisseur de la bordure bleue sur le dessus et supprimer la bordure de tous les autres côtés (comme l'image d'elysium dans votre réponse), que devrais-je changer? Je suis nouveau sur WPF, et par conséquent la question
mrid
49

La solution que j'ai finalement choisie était MahApps.Metro ( github ), qui (après l'avoir utilisé sur deux logiciels maintenant), je considère un excellent kit d'interface utilisateur (merci à Oliver Vogel pour la suggestion) .

Style de fenêtre

Il enveloppe l'application avec très peu d'effort et comporte des adaptations des contrôles standard de Windows 8. C'est très robuste.

Filigrane de zone de texte

Une version est disponible sur Nuget:

Vous pouvez installer MahApps.Metro via Nuget en utilisant l'interface graphique (clic droit sur votre projet, Gérer les références Nuget, rechercher 'MahApps.Metro') ou via la console:

PM> Package d'installation MahApps.Metro

C'est également gratuit , même pour un usage commercial.

Mise à jour 29/10/2013:

J'ai découvert que la version Github de MahApps.Metro regorge de commandes et de styles qui ne sont pas disponibles dans la version actuelle de nuget, notamment:

Datagrids:

entrez la description de l'image ici

Nettoyer la fenêtre:

entrez la description de l'image ici

Flyouts:

entrez la description de l'image ici

Carrelage:

entrez la description de l'image ici

Le référentiel github est très actif avec pas mal de contributions d'utilisateurs. Je recommande de le vérifier.

Daniel
la source
Je le teste, gentil +1 :)
Akrem
3
très belle mise à jour! J'essaye aussi MahApps.Metro, l'interface utilisateur moderne pour WPF et Elysium.J'ai trouvé qu'Elysium est si compliqué à utiliser et confondre sur leur site Web / Doc .. Interface utilisateur moderne et MahApps.Metro est léger et facile à utiliser, mais MahApps. Metro plus compétitif sur les contrôles de formulaire WPF.
Cheung
Est-il très difficile de créer ma propre personnalisation des composants, au lieu d'utiliser MahApps?
Matheus Saraiva
42

Je recommanderais Modern UI pour WPF .

Il a un mainteneur très actif, c'est génial et gratuit!

Interface utilisateur moderne pour WPF (capture d'écran de l'exemple d'application

Je porte actuellement certains projets sur MUI, la première (et la deuxième) impression est juste wow!

Pour voir MUI en action, vous pouvez télécharger XAML Spy qui est basé sur MUI.

ÉDITER: Utiliser l'interface utilisateur moderne pour WPF quelques mois et j'adore ça!

Joël
la source
16

Sur la base de la réponse de Viktor La Croix avec la source ci-dessus, je la changerais pour utiliser ce qui suit:

Exemple de police Marlett

Il est préférable d'utiliser la police Marlett plutôt que les points de données de chemin pour les boutons Réduire, Restaurer / Agrandir et Fermer.

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True" Grid.Row="0">
<Button Command="{Binding Source={x:Static SystemCommands.MinimizeWindowCommand}}" ToolTip="minimize" Style="{StaticResource WindowButtonStyle}">
    <Button.Content>
        <Grid Width="30" Height="25">
            <TextBlock Text="0" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="3.5,0,0,3" />
        </Grid>
    </Button.Content>
</Button>
<Grid Margin="1,0,1,0">
    <Button x:Name="Restore" Command="{Binding Source={x:Static SystemCommands.RestoreWindowCommand}}" ToolTip="restore" Visibility="Collapsed" Style="{StaticResource WindowButtonStyle}">
        <Button.Content>
            <Grid Width="30" Height="25" UseLayoutRounding="True">
                <TextBlock Text="2" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="2,0,0,1" />
            </Grid>
        </Button.Content>
    </Button>
    <Button x:Name="Maximize" Command="{Binding Source={x:Static SystemCommands.MaximizeWindowCommand}}" ToolTip="maximize" Style="{StaticResource WindowButtonStyle}">
        <Button.Content>
            <Grid Width="31" Height="25">
                <TextBlock Text="1" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="2,0,0,1" />
            </Grid>
        </Button.Content>
    </Button>
</Grid>
<Button Command="{Binding Source={x:Static SystemCommands.CloseWindowCommand}}" ToolTip="close"  Style="{StaticResource WindowButtonStyle}">
    <Button.Content>
        <Grid Width="30" Height="25">
            <TextBlock Text="r" FontFamily="Marlett" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="0,0,0,1" />
        </Grid>
    </Button.Content>
</Button>

VolerMaverick
la source
Salut Flying Maverick. Pourriez-vous expliquer pourquoi il est préférable d'utiliser la police marlett? J'ai trois implémentations différentes et je ne sais pas laquelle utiliser. Le premier utilise des points de données de chemin, le second utilise marlett et le troisième est une recréation des boutons au format SVG. J'essaie d'utiliser les meilleures pratiques à 100% dans ce projet et je ne sais pas laquelle est la meilleure option. Pouvez-vous expliquer pourquoi marlett est meilleur?
user1632018
1
Salut user1632018 Si vous cherchez à créer une fenêtre chrome personnalisée dans Winform ou WPF, vous devriez jeter un œil à la police «Marlett» disponible sur votre système. Cette police contient les glyphes réels utilisés dans Windows pour les boutons Réduire, Agrandir, Restaurer et Fermer. L'utilisation de cette police facilite la réutilisation de ces glyphes dans une fenêtre chrome personnalisée, au lieu d'images personnalisées généralement utilisées. Vous pouvez jeter un œil à la police Marlett dans la table de caractères Windows ou sur le lien suivant pour plus de détails: microsoft.com/typography/fonts/font.aspx?FMID=1264 J'espère que cela vous aidera.
FlyingMaverick
2

Si vous êtes prêt à payer, je vous recommande fortement les composants Telerik pour WPF . Ils offrent d'excellents styles / thèmes et des thèmes spécifiques pour les deux, Office 2013 et Windows 8 (EDIT: et aussi un style à thème Visual Studio 2013). Cependant, il offre beaucoup plus que de simples styles en fait, vous obtiendrez tout un tas de contrôles qui sont vraiment utiles.

Voici à quoi cela ressemble en action (captures d'écran tirées d'échantillons telerik):

Exemple de tableau de bord Telerik

Exemple de tableau de bord Telerik CRM

Voici les liens vers l' exemple de tableau de bord exécutif telerik (première capture d'écran) et ici pour le tableau de bord CRM (deuxième capture d'écran).

Ils offrent un essai de 30 jours, essayez-le!

Joël
la source
0

Jetez un œil à cette fenêtre de style métro WPF avec des bordures brillantes en option .

Il s'agit d'une application autonome n'utilisant aucune autre bibliothèque que Microsoft.Windows.Shell (incluse) pour créer des fenêtres de style métro avec des bordures brillantes facultatives.

Prend en charge Windows jusqu'à XP (.NET4).

Laila
la source