Archive

Archive for April, 2009

Rahmenlose Fenster mit WPF

April 20th, 2009 Bernd 6 comments

NonRectWindow

Achtung, das folgende Beispiel bringt wahrscheinlich keinen Nutzen für den täglichen Umgang mit WPF!

EGAL! Manchmal muss man eben auch spielen :-) . Rahmenlose Fenster sind einfach cool. Wenn dann auch noch ein guter Designer mit im Spiel ist, sieht das Ganze auch besser aus als bei meinem Versuch.

Ok, Design beiseite. Was ist zu tun um eine Anwendung mit einem rahmenlosen Fenster zu erstellen?

Der erste Schritt besteht darin, an dem Fenster der Applikation drei Eigenschaften entsprechend zu setzen:

  • AllowsTransparency auf true
  • WindowStyle auf None – in Verbindung mit AllowsTransparency=”True” wird erreicht, dass der Fensterrahmen und die Titelzeile verschwinden
  • Background auf Transparent
    <Window x:Class="NonRectShapedWindowWPF.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="215"
        Height="215"
        AllowsTransparency="True"
        WindowStyle="None"
        Background="Transparent">
    ...

    In meinem Beispiel habe ich mich dazu entschieden das UI aus zwei Kreisen zusammenzusetzen. Der grosse Kreis dient als “Hauptrahmen” der Anwendung und der kleine Kreis enthält einen “Close” Button.

  • ...
    <Grid Height="200" Width="200">
        <Ellipse Fill="Red" Stroke="DarkRed"
                 MouseLeftButtonDown="OnCircleMouseLeftButtonDown">
            <Ellipse.BitmapEffect>
                <OuterGlowBitmapEffect GlowSize="8" GlowColor="OrangeRed" />
            </Ellipse.BitmapEffect>
        </Ellipse>
    
        <Canvas...>
    
        <Button Margin="150 -150 0 0"
                Template="{StaticResource closeButton}"
                FontFamily="Webdings"
                FontWeight="Bold"
                FontSize="10"
                Content="r"
                Click="OnCloseButtonClick"
                ToolTip="Close">
        </Button>
    </Grid>
    ...

Der eigentliche Inhalt der Anwendung versteckt sich in dem zusammengeklappten <Canvas> Element. Die Details dazu gibt es weiter unten.

Wie in dem XAML Code zu sehen ist, wird der “Close” Button über die Margin Eigenschaft an der gewünschten Stelle positioniert. Um dem Button ein rundes Aussehen zu verpassen, habe ich ein ControlTemplate erstellt.

...
<ControlTemplate x:Key="closeButton" TargetType="{x:Type Button}">
    <Grid>
        <Ellipse Fill="Red" Stroke="DarkRed"
                 Width="25" Height="25"/>

        <Label Content="{TemplateBinding Content}"
               Foreground="Black"
               HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</ControlTemplate>
...

Um dem Anwender ein Verschieben des Fensters zu ermöglichen, fügt man einen Eventhandler für das MouseLeftButtonDown Event hinzu. Und zwar an dem Element, mit dem das Fenster verschoben werden soll. Das könnte z.B. eine eigene Titelleiste sein, oder wie bei mir irgendein anderes, sichtbares Element. Der Code in dem Eventhandler ist sehr einfach:

private void OnCircleMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DragMove();
}

Ein weiterer Eventhandler wird für das Click Event unseres “Close” Buttons benötigt um das Fenster zu schliessen:

private void OnCloseButtonClick(object sender, RoutedEventArgs e)
{
    Close();
}

Damit sind die wichtigsten Schritte gemacht, um ein rahmenloses Fenster mit WPF zu erstellen. Es kann vom Anwender verschoben und geschlossen werden. Um ein bisschen Action in das Beispiel zu bringen, gibt es noch eine kleine Animation, die Text durch das Fenster scrollen lässt. Der Code dazu sieht so aus:

...
<Canvas Height="100" Width="120" ClipToBounds="True">
    <TextBlock x:Name="_text"
               Height="100" Width="120"
               VerticalAlignment="Center" HorizontalAlignment="Center"
               TextWrapping="Wrap"
               FontFamily="Tahoma" FontSize="12">
            <TextBlock.Text>
                Diese Anwendung ist völlig sinnlos. Zu nichts zu gebrauchen. Und doch hat es viel Spass
                gemacht sie zu erstellen.
            </TextBlock.Text>
            <TextBlock.Triggers>
                <EventTrigger RoutedEvent="TextBlock.Loaded" >
                  <BeginStoryboard>
                    <Storyboard>
                      <DoubleAnimation
                        Storyboard.TargetName="_text"
                        Storyboard.TargetProperty="(Canvas.Top)"
                        Duration="0:0:4"
                        From="110" To="-80"
                        RepeatBehavior="Forever"/>
                    </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </TextBlock.Triggers>
    </TextBlock>
</Canvas>
...

Das komplette Beispiel kann hier heruntergeladen werden: