Como fazer com que os aplicativos WPF tenham aparência Metro, mesmo no Windows 7? (Janela Chrome / Tema / Tema)


123

Gosto do chrome da janela no novo Office Suite e Visual Studio:

insira a descrição da imagem aqui

Ainda estou desenvolvendo aplicativos para o Windows 7, é claro, mas estou imaginando se existe uma maneira rápida e fácil (leia-se: estilo WPF ou biblioteca do Windows) para emular esse estilo. Eu fiz alguns estilos de cromo de janela no passado, mas fazer com que pareça e se comporte da maneira certa é realmente complicado.

Alguém sabe se existem modelos ou bibliotecas existentes para adicionar uma aparência de "UI moderna" aos meus aplicativos WPF?


8
Este Guia / Pacote NuGet pode ser útil: MahaApps Metro Ele contém um conjunto de estilos e controles para criar aplicativos WPF com aparência e funcionalidade do Metro.
27612 Oliver Vogel

As perguntas que nos pedem para recomendar ou encontrar um livro, ferramenta, biblioteca de software, tutorial ou outro recurso externo não são tópicos do Stack Overflow, pois tendem a atrair respostas e spam opinativos. Em vez disso, descreva o problema e o que foi feito até agora para resolvê-lo.
Scott Solmer

Respostas:


149

O que eu fiz foi criar minha própria janela e estilo. Porque eu gosto de ter controle sobre tudo e não queria algumas bibliotecas externas apenas para usar uma janela. Eu olhei para o MahApps.Metro já mencionado no GitHub

MahApps

e também uma interface moderna e muito boa no GitHub . (Somente .NET4.5)

UI moderna

Há mais um Elysium, mas eu realmente não tentei este.

Elysium

O estilo que eu fiz foi muito fácil quando eu olhei como é feito nesses. Agora eu tenho minha própria janela e posso fazer o que quiser com o xaml ... para mim, é a principal razão pela qual fiz a minha. E eu fiz mais um para você também :) Eu provavelmente deveria dizer que não seria capaz de fazer isso sem explorar a interface moderna , foi uma grande ajuda. Tentei fazer com que parecesse a janela do VS2012. Se parece com isso.

Minha janela

Aqui está o código (observe que ele está direcionado ao .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);
    }
}

E aqui recursos:

<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>

1
Olá e muito obrigado por este ótimo código que você postou. Apenas um favor para perguntar: é possível ter uma sombra na janela? A única coisa que descobri é mudar GlassFrameThicknesspara 1. Mas a sombra é muito forte e escura. Como posso alterar o peso e a opacidade?
Xperator 23/05


É muito difícil criar minha própria personalização de componentes, em vez de usar o MahApps?
Matheus Saraiva

Fantástico! Muito obrigado por esta excelente contribuição, tentei fazer o mesmo muitas vezes, mas nunca obtive um resultado tão perfeito.
Leodev

Suponha que eu queira aumentar a espessura da borda azul na parte superior e removê-la em todos os outros lados (como a foto de elísio na sua resposta), o que eu precisaria mudar? Eu sou novo para WPF, e, portanto, a questão
mrid

49

A solução que acabei escolhendo foi o MahApps.Metro ( github ), que (depois de usá-lo em dois softwares agora) considero um excelente kit de interface do usuário (crédito a Oliver Vogel pela sugestão) .

Estilo da janela

Ele examina o aplicativo com muito pouco esforço necessário e possui adaptações dos controles padrão do Windows 8. É muito robusto.

Marca d'água da caixa de texto

Uma versão está disponível no Nuget:

Você pode instalar o MahApps.Metro via Nuget usando a GUI (clique com o botão direito do mouse em seu projeto, Gerenciar referências do Nuget, procure por 'MahApps.Metro') ou através do console:

PM> Pacote de Instalação MahApps.Metro

Também é gratuito - mesmo para uso comercial.

Atualização 10-29-2013:

Descobri que a versão do MahApps.Metro para Github está repleta de controles e estilos que não estão disponíveis na versão atual do nuget, incluindo:

Datagrids:

insira a descrição da imagem aqui

Janela limpa:

insira a descrição da imagem aqui

Flyouts:

insira a descrição da imagem aqui

Azulejos:

insira a descrição da imagem aqui

O repositório do github é muito ativo, com algumas contribuições do usuário. Eu recomendo dar uma olhada.


Eu testá-lo, agradável +1 :)
Akrem

3
atualização muito legal! Eu também experimento o MahApps.Metro, UI moderna para WPF e Elysium.Eu achei que o Elysium é tão complicado de usar e confundir em seu site / Doc .. A UI moderna e o MahApps.Metro são leves e fáceis de usar, mas o MahApps. Metro mais competitivo nos controles de formulário do WPF.
Cheung

É muito difícil criar minha própria personalização de componentes, em vez de usar o MahApps?
Matheus Saraiva

42

eu recomendaria UI moderno para WPF .

Ele tem um mantenedor muito ativo, é incrível e gratuito!

UI moderna para WPF (captura de tela do aplicativo de amostra

Atualmente, estou portando alguns projetos para o MUI, a primeira (e a segunda) impressão é apenas uau!

Para ver o MUI em ação, você pode baixar o XAML Spy, que é baseado no MUI.

Edição: Usando Modern UI para WPF há alguns meses e eu estou adorando!


16

Baseado na resposta de Viktor La Croix com a fonte acima, eu mudaria para usar o seguinte:

Exemplo de fonte Marlett

É uma prática melhor usar a fonte Marlett em vez dos pontos Path Data para os botões Minimizar, Restaurar / Maximizar e Fechar.

<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>


Oi Flying Maverick. Você seria capaz de explicar por que é uma prática melhor usar a fonte marlett? Eu tenho três implementações diferentes e não tenho certeza de qual usar. O primeiro está usando pontos de dados do caminho, o segundo está usando marlett e o terceiro é uma recriação dos botões no formato SVG. Estou tentando usar 100% das melhores práticas neste projeto e não tenho certeza de qual é a melhor opção. Você poderia explicar por que Marlett é melhor?
User1632018

1
Olá, user1632018 Se você deseja criar uma janela cromada personalizada no Winform ou no WPF, consulte a fonte 'Marlett' que está disponível no seu sistema. Essa fonte contém os glifos reais usados ​​no Windows para os botões Minimizar, Maximizar, Restaurar e Fechar. O uso dessa fonte facilita muito a reutilização desses glifos em uma janela cromada personalizada, em vez de imagens personalizadas normalmente usadas. Você pode dar uma olhada na fonte Marlett no Mapa de Caracteres do Windows ou no link a seguir para obter mais detalhes: microsoft.com/typography/fonts/font.aspx?FMID=1264 Espero que isso ajude.
FlyingMaverick

2

Se você estiver disposto a pagar, recomendo fortemente o Telerik Components for WPF . Eles oferecem ótimos estilos / temas e existem temas específicos para o Office 2013 e Windows 8 (EDIT: e também um estilo com tema do Visual Studio 2013). No entanto, oferecendo muito mais do que apenas estilos, na verdade, você terá um monte de controles que são realmente úteis.

Aqui está como fica em ação (Capturas de tela tiradas de amostras telerik):

Telerik Dashboard Sample

Exemplo de painel do Telerik CRM

Aqui estão os links para a amostra do painel executivo da telerik (primeira captura de tela) e aqui para o CRM Dashboard (segunda captura de tela).

Eles oferecem um teste de 30 dias, apenas tente!


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.