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)); }
March 29th, 2007 at 08:49
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
March 29th, 2007 at 09:08
Hallo Natascha,
habe die usings hinzugefügt
March 20th, 2008 at 21:46
Das hat mir sehr weitergeholfen! Dank an den Autor!
May 15th, 2008 at 11:10
So ist das Leben!