2010-07-30 5 views
21

Ich habe ein WPF-Fenster mit WindowStyle auf keine gesetzt. Gibt es eine Möglichkeit, wie ich dieses Fenster zwingen kann, einen Schatten fallen zu lassen (wie den, den man bekommt, wenn WindowStyle nicht vorhanden ist)? Ich möchte AllowTransparency nicht auf "true" setzen, da dies die Leistung beeinträchtigt. Und ich möchte auch nicht das Hardware-Rendering deaktivieren (ich habe irgendwo gelesen, dass Transparenz besser funktioniert, wenn es deaktiviert ist).DropShadow für WPF Borderless Window

Antwort

31

ich ein kleines Utility-Klasse geschrieben haben, die in der Lage ist, genau das zu tun, was Sie wollen: ein Standard-Schatten über einem grenzenlosen Window fallen aber AllowsTransparency Satz zu false haben.

Sie müssen nur die DropShadowToWindow(Window window) Methode aufrufen. Es ist bevorzugt, dass Sie diesen Aufruf direkt nach dem Konstruktor des Fensters InitializeComponent() ausführen, aber es wird funktionieren, auch wenn Sie es aufrufen, nachdem das Fenster angezeigt wird.

using System; 
using System.Drawing.Printing; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Interop; 

public static class DwmDropShadow 
{ 
    [DllImport("dwmapi.dll", PreserveSig = true)] 
    private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); 

    [DllImport("dwmapi.dll")] 
    private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); 

    /// <summary> 
    /// Drops a standard shadow to a WPF Window, even if the window is borderless. Only works with DWM (Windows Vista or newer). 
    /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect, 
    /// as AllowsTransparency involves a huge performance issue (hardware acceleration is turned off for all the window). 
    /// </summary> 
    /// <param name="window">Window to which the shadow will be applied</param> 
    public static void DropShadowToWindow(Window window) 
    { 
     if (!DropShadow(window)) 
     { 
      window.SourceInitialized += new EventHandler(window_SourceInitialized); 
     } 
    } 

    private static void window_SourceInitialized(object sender, EventArgs e) 
    { 
     Window window = (Window)sender; 

     DropShadow(window); 

     window.SourceInitialized -= new EventHandler(window_SourceInitialized); 
    } 

    /// <summary> 
    /// The actual method that makes API calls to drop the shadow to the window 
    /// </summary> 
    /// <param name="window">Window to which the shadow will be applied</param> 
    /// <returns>True if the method succeeded, false if not</returns> 
    private static bool DropShadow(Window window) 
    { 
     try 
     { 
      WindowInteropHelper helper = new WindowInteropHelper(window); 
      int val = 2; 
      int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4); 

      if (ret1 == 0) 
      { 
       Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 }; 
       int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m); 
       return ret2 == 0; 
      } 
      else 
      { 
       return false; 
      } 
     } 
     catch (Exception ex) 
     { 
      // Probably dwmapi.dll not found (incompatible OS) 
      return false; 
     } 
    } 
} 
+2

Das ist großartig, außer ich bekomme ein Problem, wo ein Kind-Fenster (auch mit einem Dropshadow) zu öffnen, reduziert den Schlagschatten auf dem Eltern, und dann, wenn das Kindfenster geschlossen ist, entfernt es vollständig. seltsamer Bug, nicht gefunden, was es noch verursacht. Es scheint auch zu tun, wenn Kindwindow nicht Dropshadow verwendet) –

+1

Arbeitete wie ein Charme. :) Vielen Dank. –

+2

Hinweis: Als ich versuchte, diesen Ansatz in einer nativen App zu verwenden, erschien der Schatten erst, als ich die Ränder ungleich Null anwendete. (Für ein randloses Fenster scheinen die tatsächlichen Werte keine Rolle zu spielen, solange sie sich von 0 unterscheiden). –

3

Wenn Sie zulassen, dass das Fenster Größenbeschränkungen hat, indem Sie ResizeMode auf CanResize setzen, erhalten Sie den OS-Schlagschatten. Sie können dann die Werte MaxWidth, MinWidth, MaxHeight und MinHeight auf Werte festlegen, die die Größenänderung verhindern.

Wenn Sie ein randloses Fenster ohne Stil haben, müssen Sie das gesamte Erscheinungsbild für das Fenster in Ihrem eigenen visuellen Baum bereitstellen, einschließlich eines Schlagschattens, da diese Kombination von Einstellungen die gleiche ist will was das OS bietet.

EDIT:

Von diesem Punkt, wenn Ihr Fenstergröße festgelegt ist, einfach den Schlagschatten hinzufügen, vielleicht als <Rectangle/> als das erste Element in dem Inhalt eines <Canvas/>

etwas wie folgt aus:

<Window x:Class="MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" AllowsTransparency="True" Background="Transparent" WindowStyle="None"> 
    <Canvas> 
     <Rectangle Fill="#33000000" Width="100" Height="100"/> 
     <Rectangle Fill="#FFFF0000" Width="95" Height="95" /> 
    </Canvas> 
</Window> 

Beachten Sie, dass die Fill Eigenschaft des ersten Rectangle teilweise transparent ist, die Sie auch mit der Opacity Eigenschaft der 012 tun könnten. Sie können eine eigene Grafik oder eine andere Form verwenden, um das Erscheinungsbild des Schlagschattens anzupassen.

Beachten Sie, dass dies Ihre Anforderung zu AllowsTransparencyFalse verletzt, aber Sie haben keine Wahl: Wenn Sie Transparenz wollen, müssen Sie es zulassen.

+0

Wie mache ich das letztere? – TripShock

+0

In Bezug auf Edit: Ich habe so etwas versucht, aber es wirklich Leistung tötet. Ich mache das auf Windows XP, weiß nicht über Vista/7. – TripShock

+0

Sie müssen nicht unbedingt zwischen der Einstellung 'AllowsTransparency' auf' False' und dem Löschen eines Schattens wählen. Sie können 'AllowsTransparencey' auf' True' setzen, nur in 4 Fenstern an den Rändern Ihres Hauptfensters, das dafür zuständig ist, den Schatten fallen zu lassen. Auf diese Weise bleibt die Hauptfensterleistung erhalten. – cprcrack

5

Verwenden Sie die Microsoft WPF Shell Integration Library, einfacher und mit einer besseren Leistung

+0

3 Jahre später ist die Verbindung tot. –

+0

@NickeManarin: Eine einfache Suche besagt, dass die Bibliothek nicht mehr benötigt wird, da sie in System.Windows und System.Windows.Shell in .NET 4.5 implementiert ist https://mui.codeplex.com/workitem/19695 –

+1

NuGet: Install -Package Microsoft.Windows.Shell – Carol

5

Patricks Antwort funktioniert gut, außer wenn ein Win32-Fenster gehostet wird. Wenn dies passiert, stellen Sie fest, dass das gehostete Fenster "ausgewaschen" ist (es sieht so aus, als ob Windows den Effekt "Glasplatte" auf das gesamte gehostete Fenster anwendet). Dieses ungerade Verhalten wird behoben, wenn die Struktur lokal definiert wird, z. B. , z.

+2

Das ist ein wirklich guter Punkt. Es ist auch gut zu bemerken, dass dieses Problem nur auf einer kleinen Untermenge von Maschinen reproduziert werden kann, so dass es besonders unangenehm ist. –

+0

Bisher habe ich es auf einer Intel HD 520 Grafikkarte, sowie anderen Intel HD Karten auf anderen Laptops bemerkt. –

+0

Wenn Sie 'Margins' in' class' konvertieren, kommt das Problem zurück. Es muss mit der Tatsache zusammenhängen, dass 'System.Drawing.Printing' ebenfalls eine' Klasse' ist. –

0

Warum nicht einfach den Schatten mit dem gleichen Objekt wie Ihr "Fenster" erstellen, sondern größer und dahinter.

<Window x:Class="WPF_Custom_Look.ShadowWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="ShadowWindow" Height="400" Width="450" ResizeMode="NoResize" Background="Transparent" AllowsTransparency="True" WindowStyle="None"> 
<Grid> 
    <Rectangle Fill="Black" Width="330" Opacity="0.5" Height="279"> 
     <Rectangle.Effect> 
      <BlurEffect Radius="30"/> 
     </Rectangle.Effect> 
    </Rectangle> 
    <Rectangle Fill="#FFFDFDFD" Width="312" Height="260"/> 

</Grid> 

Oder wenn Sie einen transparenten Titelleiste benötigen, könnte es durch eine <Border>

<Canvas> 
    <Border BorderBrush="Black" BorderThickness="7" Height="195" Width="304" Canvas.Left="53" Canvas.Top="25"> 
     <Border.Effect> 
      <BlurEffect Radius="20"/> 
     </Border.Effect> 
    </Border> 
    <Rectangle Fill="#FF86B0F9" Width="285" Height="177" Opacity="0.7" Canvas.Left="62" Canvas.Top="34" MouseDown="Border_MouseDown"/> 
    <Rectangle Fill="#FFFDFDFD" Width="285" Height="143" Canvas.Left="62" Canvas.Top="68"/> 
</Canvas> 

Bearbeiten ersetzt werden: Ich habe bemerkt, OP will AllowsTransparency auf False. Ich kann keinen Schatten sehen, ohne zu arbeiten, ohne dass es "wahr" ist.

Verwandte Themen