HowTo: Rahmenlose WPF Apps mit Schattenwurf
Die Standard-Fenster von Windows verschwinden immer mehr und werden ersetzt durch schicker anmutende Bedienelemente. Diesmal meine ich allerdings keine “Metro” Apps, sondern normale, full-power Windows Applikationen.
Achtung: Der Blogpost ist sehr “Low-Level”. Es gibt bestimmt einige Libraries oder NuGet Packages, die diese Sachen mitbringen. Die herangehensweise hat auch 2 Nachteile!
Aus dem Drögen Standard (für Windows Desktop Applikationen):
Wird zum Beispiel sowas:
1. Schritt: WPF Window Rahmenlos machen
Um ein “Window” komplett rahmenlos zu bekommen muss man die Eigenschaften “WindowStyle” auf “none” setzen und den “ResizeMode” auf “NoResize”.
<Window x:Class="DropShadow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
</Grid>
</Window>
Ergebnis:
Man hat nun eine gänzlich weiße Fläche.
2. Schritt: Den Rahmen erzeugen
Ich hatte erst über diverse Styles es probiert, allerdings hab ich dann diese Lösung gefunden, welche direkt Betriebssystem Funktionen nutzt. Vorteil: Auf Windows 8 sieht es “dezenter” aus als auf Windows 7 – sprich: Es passt besser in das Gesamtbild des Betriebssystem.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
public int leftWidth;
public int rightWidth;
public int topHeight;
public int bottomHeight;
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var helper = new WindowInteropHelper(this);
int val = 2;
DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
var m = new MainWindow.Margins { bottomHeight = -1, leftWidth = -1, rightWidth = -1, topHeight = -1 };
DwmExtendFrameIntoClientArea(helper.Handle, ref m);
IntPtr hwnd = new WindowInteropHelper(this).Handle;
}
[DllImport("dwmapi.dll", PreserveSig = true)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);
}
Resultat:
Was man dabei beachten sollte:
Durch das rahmenlose Fenster muss man sich natürlich jetzt selber darum kümmern, dass der Anwender es vergrößern bzw. verkleinern kann und das man das Fenster verschieben kann. Ich vermute es gibt hier (wie oben erwähnt) irgendwelche Frameworks/Libraries/NuGet Packges die einem das Leben vereinfachen.
Die kompletten Sourcen findet ihr auf GitHub.







Otto Gierling
11. November 2012
Zum Verschieben des Fensters gibt es DragMove(); z.B. so
XAML:
CodeBehind:
private void Window_OnMouseleftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
Martin
11. November 2012
Hast damit aber noch einige Probleme. Zum Beispiel fehlt die AeroSnap-Funktion.
Ich verwende momentan MahApps.Metro. Ist auch nicht perfekt, aber schon sehr nah dran.
Robert Mühsig
12. November 2012
Ich hab es selbst nur für einen Splashscreen benutzt – daher reichte mir diese “low-level” Variante.
Würdest du MahApps.Metro nicht weiter empfehlen? Die Demo App sieht doch ganz nett aus und auf der schnelle hab ich jetzt kein fehlendes “Windows-Standard-Fenster”-Feature gefunden.
Martin
12. November 2012
Empfehlen kann man es schon. Gibt nur so ein paar kleine Sachen. Zum Beispiel sollte man die Controls.xaml nicht in der App.xaml als Ressource angeben, da ansonsten die Tooltips nicht mehr lesbar sind.