Glas Effekte unter WPF

March 1st, 2007

Da ich lange geforscht habe, wie man in WPF Anwendungen den Glaseffekt von Windows Vista(tm) verwenden kann wollte ich euch die Lösung nicht vorenthalten.

Hier also meine kleine Beispiel-Klasse, mit der man die Glas Effekte für Windows Presentation Foundation Anwendungen nutzen kann.

Wir benötigen zunächst Zugriff auf die DWM API:

using System.Runtime.InteropServices;

static class DWMApi
{
    // Die Windows Message Konstanten benötigen wir später,
    // um auf Änderungen reagieren zu können
    public const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
    public const int WM_DWMNCRENDERINGCHANGED = 0x031F;
    public const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
    public const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321;

    // Die Margins Struktur mit der wir festlegen welcher Bereich
    // vom Fenster den Glas Effekt erhalten soll
    public struct MARGINS
    {
         public MARGINS(Thickness t)
         {
             Left = (int)t.Left;
             Right = (int)t.Right;
             Top = (int)t.Top;
             Bottom = (int)t.Bottom;
         }
         public int Left;
         public int Right;
         public int Top;
         public int Bottom;
    }

    // Diese Funktion aktiviert den Glas Effekt
    [DllImport("dwmapi.dll")]
    public static extern void DwmExtendFrameIntoClientArea(
                              IntPtr hwnd, ref MARGINS margins);
    // Diese Funktion prüft ob der Glas Effekt im System
    // überhaupt aktiv ist
    [DllImport("dwmapi.dll")]
    public static extern bool DwmIsCompositionEnabled();
}

Hier nun eine kleine Collection Klasse GlassWindows für alle Fenster die später den Glas Effekt bekommen sollen.

Sie verwaltet die Fenster und legt einen Hook auf Windows Nachrichten für jedes Fenster an. Damit können wir später reagieren, sollte während der Laufzeit unserer Anwendung der Glas Effekt im System kurzzeitig ausgeschlalten werden.

using System.Collections.Generic;
using System.Windows.Interop;
using System.Windows.Media;

class GlassWindows
{

    // Die Generische Liste für unsere Fenster
    private List<WindowItem> windows = new List<WindowItem>();

    // Diese Funktion fügt ein Fenster und den Bereich vom
    // Glas Effekt zu unsere Liste hinzu
    public void Add(Window window, Thickness margin)
    {
        //  Instanz unserer Item Klasse erstellen
        WindowItem wi = new WindowItem();
        // Handle der WPF "Window" Klasse herausfinden
        wi.Handle = new WindowInteropHelper(window).Handle;
        // Glas Bereich zuweisen
        wi.margin = margin;
        // WPF "Window" zuweisen
        wi.window = window;
        // Listeneintrag anlegen
        windows.Add(wi);
        // Winows Nachrichten Hook hinzufügen
        HwndSource.FromHwnd(wi.Handle).AddHook(WndProc);
        // Glas Effekt einschalten
        EnableGlass(wi, true);
    }

    // Gibt den Listen Eintrag anhand des Fenster Handles zurück
    // Damit können wir später wenn die Windows Nachricht
    // eintrifft zuordnen zu welchem Eintrag diese gehört.
    private WindowItem WindowItemByHandle(IntPtr Handle)
    {
        // Schleife durch alle Fenster in der Liste
        foreach (WindowItem wi in windows)
        {
            // Wenn Handle übereinstimmt aktuellen Eintrag
            // zurückgeben und Fuktion verlassen
            if (wi.Handle == Handle)
                return wi;
        }
        return null;
    }

    // Glass Effekt an- bzw. ausschalten
    private void EnableGlass(WindowItem windowitem, bool enabled)
    {
        if (enabled && DWMApi.DwmIsCompositionEnabled())
        {
            // Hintergrundfarbe des Fensters
            // auf Transparent stellen
            windowitem.window.Background = Brushes.Transparent;
            // Die Farbe festlegen auf die der Glas Effekt
            // angewendet werden soll (Transparent)
            HwndSource.FromHwnd(windowitem.Handle).
                CompositionTarget.BackgroundColor
                    = Colors.Transparent;
            // Den Bereich für den Glas Effekt definieren
            DWMApi.MARGINS margins =
                       new DWMApi.MARGINS(windowitem.margin);
            // Glass Effekt aktivieren
            DWMApi.DwmExtendFrameIntoClientArea
                       (windowitem.Handle, ref margins);
        }
        else
        {
            // Hintergrundfarbe des Fensters zurück auf die
            // Systemfarbe stellen
            windowitem.window.Background
                = SystemColors.WindowBrush;
        }
    }

    // Windwos Nachrichten abfangen (Hook)
    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
                           IntPtr lParam, ref bool handled)
    {
        // Wenn Glass effekt ein- oder ausgeschalten
        // wurde entsprechend behandeln
    if (msg == DWMApi.WM_DWMCOMPOSITIONCHANGED)
        {
            EnableGlass(WindowItemByHandle(hwnd),
                        DWMApi.DwmIsCompositionEnabled());
            handled = true;
        }
        return IntPtr.Zero;
    }

}
// Kleine Klasse als Item für unsere Collection
class WindowItem
{
    public IntPtr Handle;
    public Window window;
    public Thickness margin;
}

So nun müssen wir das ganze nur noch in unserer Anwendung benutzen. Dazu fügen wir im Konstruktor der App Klasse (App.xaml.cs) folgendes hinzu.

public partial class App : System.Windows.Application
{
    public App()
    {
        // Neue Instanz unserer GW Klasse anlegen
        GlassWindows gw = new GlassWindows();
        // GW Instanz als Globale Eigenschafft mitführen
        this.Properties["GlassWindows"] = gw;
    }

}

Jetzt brauchen wir nur noch im jeweiligen Fenster den "OnSourceInitialized" Event überschreiben und unser Fenster in unserer GW Collection hinzufügen.

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    ((GlassWindows)App.Current.
         Properties["GlassWindows"]).
             Add(this, new Thickness(-1));
}

4 Responses to “Glas Effekte unter WPF”

  1. Natascha Says:

    Hallo Christian,

    ich habe versucht dein Beispiel auszuprobieren, hat aber daran gescheitert, dass ich nicht weiss in welcher Klasse welche usings notwendig sind. Dadurch erkennt das Programm manche Objekte nicht. Wenn du dein Beispiel mit den usings ergänzen könntest, wäre ich dir sehr dankbar.

    Natascha

  2. Orbmu2k Says:

    Hallo Natascha,

    habe die usings hinzugefügt :)

  3. Glasdildos Says:

    Das hat mir sehr weitergeholfen! Dank an den Autor!

  4. Ralf Says:

    So ist das Leben!

Leave a Reply