Czy aplikacje WPF wyglądają jak Metro, nawet w Windows 7? (Window Chrome / Theming / Theme)

123

Podoba mi się chromowanie okna w nowym pakiecie Office i programie Visual Studio:

wprowadź opis obrazu tutaj

Oczywiście nadal tworzę aplikacje dla systemu Windows 7, ale zastanawiam się, czy istnieje szybki i łatwy sposób (czytaj: styl WPF lub biblioteka Windows) na emulację tego stylu. W przeszłości zajmowałem się stylizacją chromu na oknie, ale uzyskanie odpowiedniego wyglądu i zachowania jest naprawdę trudne.

Czy ktoś wie, czy istnieją istniejące szablony lub biblioteki umożliwiające dodanie wyglądu i stylu „nowoczesnego interfejsu użytkownika” do moich aplikacji WPF?

Daniel
źródło
8
Ten przewodnik / pakiet NuGet może być pomocny: MahaApps Metro Zawiera zestaw stylów i kontrolek do tworzenia aplikacji WPF z wyglądem i działaniem Metro.
Oliver Vogel
Pytania proszące nas o rekomendację lub znalezienie książki, narzędzia, biblioteki oprogramowania, samouczka lub innego zasobu poza witryną nie są tematem dla Stack Overflow, ponieważ zwykle przyciągają uparte odpowiedzi i spam. Zamiast tego opisz problem i opisz dotychczasowe działania, aby go rozwiązać.
Scott Solmer

Odpowiedzi:

149

Stworzyłem własne okno i styl. Ponieważ lubię mieć kontrolę nad wszystkim i nie chciałem, aby zewnętrzne biblioteki używały z tego okna. Spojrzałem na wspomniane już MahApps.Metro na GitHubie

MahApps

a także bardzo ładny nowoczesny interfejs użytkownika na GitHubie . (Tylko .NET4.5)

Nowoczesny interfejs użytkownika

Jest jeszcze jeden, Elysium, ale naprawdę nie próbowałem tego.

Elizjum

Styl, który zrobiłem, był naprawdę łatwy, kiedy patrzyłem, jak to się robi w tych. Teraz mam własne okno i mogę robić, co chcę z XAML ... dla mnie jest to główny powód, dla którego zrobiłem własne. I zrobiłem dla Ciebie jeszcze jeden :) Powinienem chyba powiedzieć, że nie byłbym w stanie tego zrobić bez zapoznania się z nowoczesnym interfejsem użytkownika, to była bardzo pomocna. Starałem się, aby wyglądało jak okno VS2012. To wygląda tak.

Moje okno

Oto kod (pamiętaj, że jest on przeznaczony dla .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);
    }
}

A tutaj zasoby:

<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
źródło
1
Cześć i bardzo dziękuję za ten świetny kod, który opublikowałeś. Tylko prośba o przysługę, czy można mieć cień na oknie? Jedyną rzeczą, zorientowali się, zmienia GlassFrameThicknesssię 1. Ale cień jest zbyt silny i ciemny. Jak mogę zmienić jego wagę i krycie?
xperator
Czy tworzenie własnych dostosowań komponentów zamiast korzystania z MahApps jest bardzo trudne?
Matheus Saraiva
Fantástico! Bardzo dziękuję za ten wspaniały wkład, wielokrotnie próbowałem zrobić to samo, ale nigdy nie uzyskałem tak doskonałego wyniku.
Leodev
Załóżmy, że chcę zwiększyć grubość niebieskiej ramki u góry i usunąć obramowanie ze wszystkich pozostałych stron (jak na zdjęciu elizjum w Twojej odpowiedzi), co musiałbym zmienić? Jestem nowym do WPF, i stąd pytanie
mrid
49

Rozwiązanie, które ostatecznie wybrałem, to MahApps.Metro ( github ), które (po użyciu go teraz na dwóch programach) uważam za doskonały zestaw UI ( dzięki sugestii Olivera Vogela ) .

Styl okna

Skórki aplikacji wymagają bardzo niewielkiego wysiłku i mają dostosowania standardowych elementów sterujących systemu Windows 8. Jest bardzo wytrzymały.

Znak wodny pola tekstowego

Wersja jest dostępna w Nuget:

Możesz zainstalować MahApps.Metro przez Nuget za pomocą GUI (kliknij prawym przyciskiem myszy swój projekt, Zarządzaj referencjami Nuget, wyszukaj `` MahApps.Metro '') lub za pomocą konsoli:

PM> Install-Package MahApps.Metro

Jest to również bezpłatne - nawet do użytku komercyjnego.

Aktualizacja 29.10.2013:

Odkryłem, że wersja MahApps.Metro na Githubie jest wypełniona kontrolkami i stylami, które nie są dostępne w aktualnej wersji nuget, w tym:

Datagrids:

wprowadź opis obrazu tutaj

Wyczyść okno:

wprowadź opis obrazu tutaj

Wyskakujące okienka:

wprowadź opis obrazu tutaj

Płytki:

wprowadź opis obrazu tutaj

Repozytorium github jest bardzo aktywne z dużą ilością wkładów użytkowników. Polecam to sprawdzić.

Daniel
źródło
Testuję
3
bardzo fajna aktualizacja! Wypróbowuję również MahApps.Metro, Modern UI for WPF i Elysium. Odkryłem, że Elysium jest tak skomplikowane w użyciu i mylone na ich stronie internetowej / Doc .. Nowoczesny UI i MahApps.Metro jest lekki i łatwy w użyciu, ale MahApps. Metro bardziej konkurencyjne w kontrolkach formularzy WPF.
Cheung
Czy tworzenie własnych dostosowań komponentów zamiast korzystania z MahApps jest bardzo trudne?
Matheus Saraiva
42

Polecam nowoczesny interfejs użytkownika dla WPF .

Ma bardzo aktywnego opiekuna, jest niesamowity i darmowy!

Nowoczesny interfejs użytkownika dla WPF (zrzut ekranu przykładowej aplikacji

Obecnie przenoszę niektóre projekty do MUI, pierwsze (a tymczasem drugie) wrażenie jest po prostu super!

Aby zobaczyć MUI w akcji, możesz pobrać XAML Spy, który jest oparty na MUI.

EDYCJA: Używanie nowoczesnego interfejsu użytkownika dla WPF przez kilka miesięcy i bardzo mi się podoba!

Joel
źródło
16

Opierając się na odpowiedzi Viktora La Croix z powyższym źródłem, zmieniłbym to tak, aby używał następującego:

Przykład czcionki Marlett

Lepszą praktyką jest używanie czcionki Marlett zamiast punktów danych ścieżki dla przycisków Minimalizuj, Przywróć / Maksymalizuj i Zamknij.

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

FlyingMaverick
źródło
Cześć latający indywidualista. Czy byłbyś w stanie wyjaśnić, dlaczego lepiej jest używać czcionki marlett? Mam trzy różne implementacje i nie jestem pewien, której użyć. Pierwsza wykorzystuje punkty danych ścieżki, druga wykorzystuje marlett, a trzecia to odtworzenie przycisków w formacie SVG. W tym projekcie staram się wykorzystać 100% najlepszych praktyk i nie jestem pewien, która z nich jest najlepsza. Czy możesz wyjaśnić, dlaczego Marlett jest lepsza?
user1632018
1
Cześć user1632018 Jeśli chcesz stworzyć niestandardowe okno chrome w Winform lub WPF, powinieneś przyjrzeć się czcionce „Marlett”, która jest dostępna w twoim systemie. Ta czcionka zawiera rzeczywiste glify używane w systemie Windows dla przycisków Minimalizuj, Maksymalizuj, Przywróć i Zamknij. Użycie tej czcionki sprawia, że ​​ponowne użycie tych glifów w niestandardowym oknie chrome zamiast zwykle używanych niestandardowych obrazów jest naprawdę łatwe. Możesz rzucić okiem na czcionkę Marlett w mapie znaków systemu Windows lub poniższe łącze, aby uzyskać więcej informacji: microsoft.com/typography/fonts/font.aspx?FMID=1264 Mam nadzieję, że to pomoże.
FlyingMaverick
2

Jeśli chcesz zapłacić, zdecydowanie polecam Ci Telerik Components for WPF . Oferują świetne style / motywy i mają określone motywy dla obu, Office 2013 i Windows 8 (EDYCJA: a także styl tematyczny Visual Studio 2013). Jednak oferując znacznie więcej niż tylko style, w rzeczywistości otrzymasz całą masę kontrolek, które są naprawdę przydatne.

Oto jak to wygląda w akcji (zrzuty ekranu zrobione z próbek telerika):

Przykład pulpitu Telerik

Przykład pulpitu Telerik CRM

Oto linki do przykładu pulpitu menedżerskiego telerik (pierwszy zrzut ekranu), a tutaj do pulpitu CRM (drugi zrzut ekranu).

Oferują 30-dniowy okres próbny, po prostu daj mu szansę!

Joel
źródło
0

Spójrz na to okno w stylu Metro WPF z opcjonalnymi świecącymi obramowaniami .

Jest to samodzielna aplikacja, która nie korzysta z innych bibliotek niż Microsoft.Windows.Shell (w zestawie) do tworzenia okien w stylu metra z opcjonalnymi świecącymi obramowaniami.

Obsługuje Windows aż do XP (.NET4).

Laila
źródło