2010-07-30 20 views
21

Tengo una ventana WPF con WindowStyle configurado en none. ¿Hay alguna forma en que pueda forzar a esta ventana a soltar una sombra (como la que obtienes cuando WindowStyle no es ninguna)? No quiero establecer AllowTransparency en verdadero, porque afecta el rendimiento. Y tampoco quiero deshabilitar la representación de hardware (leí en alguna parte que la transparencia funciona mejor con ella deshabilitada).DropShadow for WPF Borderless Window

Respuesta

31

He escrito una pequeña clase de utilidad que es capaz de hacer exactamente lo que quiere: dejar caer una sombra estándar durante un fronteras Window pero tener AllowsTransparency conjunto de false.

sólo hay que llamar al método DropShadowToWindow(Window window). Es preferible que realice esta llamada justo después del InitializeComponent() del constructor de la ventana, pero funcionará aunque lo llame después de que se muestre la ventana.

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

Esto es genial, excepto que tengo un problema al abrir una ventana secundaria (también con una sombra) que reduce la sombreado en el elemento primario, y luego cuando se cierra la ventana hija, se elimina por completo. extraño error, no se encuentra lo que está causando aún. También parece hacerlo cuando childwindow no usa dropshadow) –

+1

Funcionó como un amuleto. :) Gracias. –

+2

Nota: cuando traté de utilizar este enfoque en una aplicación nativa, la sombra no apareció hasta que hice los márgenes distintos de cero. (Para una ventana sin bordes, los valores reales no parecen importar siempre que sean diferentes de 0). –

3

Si permite que la ventana cambie el tamaño de los bordes, estableciendo ResizeMode en CanResize, obtendrá la sombra paralela del sistema operativo. A continuación, puede establecer el MaxWidth, MinWidth, MaxHeight y MinHeight a los valores que impidan el cambio de tamaño.

Si tiene una ventana sin bordes sin un estilo, tendrá que proporcionar toda la apariencia de la ventana en su propio árbol visual, incluida una sombra paralela, ya que esta combinación de configuraciones equivale a decir que no quiere lo que el sistema operativo proporciona.

EDIT:

partir de ese momento, si el tamaño de la ventana es fija, sólo tiene que añadir dropshadow, tal vez como un <Rectangle/> como el primer elemento en el contenido de un <Canvas/>

algo como esto:

<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> 

Tenga en cuenta que la propiedad Fill de ese primer Rectangle es parcialmente transparente, que también se puede hacer con la propiedad de la OpacityRectangle. Puede usar un gráfico propio o una forma diferente para personalizar el aspecto de la sombra paralela.

Tenga en cuenta que esto infringe su requisito de tener AllowsTransparency ser False, pero no tiene otra opción: si desea transparencia, tiene que permitirlo.

+0

¿Cómo hago esto último? – TripShock

+0

En cuanto a la edición: Intenté algo como esto, pero realmente mata el rendimiento. Estoy haciendo esto en Windows XP, no sé acerca de Vista/7. – TripShock

+0

No necesariamente tiene que elegir entre establecer 'AllowsTransparency' a' False' y poder descartar una sombra. Puede establecer 'AllowsTranspareceny' en' True' solo en 4 ventanas situadas en los bordes de su ventana principal, que serán las encargadas de eliminar la sombra. De esta forma, el rendimiento de la ventana principal estará intacto. – cprcrack

5

Uso del Microsoft WPF Shell Integration Library, más fácil y con un mejor rendimiento

+0

3 años después, el enlace está muerto. –

+0

@NickeManarin: Una búsqueda sencilla dice que la biblioteca ya no es necesaria ya que se implementa en System.Windows y System.Windows.Shell en .NET 4.5 https://mui.codeplex.com/workitem/19695 –

+1

NuGet: Instalar -Paquete Microsoft.Windows.Shell – Carol

5

La respuesta de Patrick funciona muy bien, excepto cuando se aloja una ventana win32. Cuando eso ocurre, observa que la ventana alojada está "lavada" (parece que Windows está aplicando el efecto de "hoja de vidrio" a toda la ventana alojada). Este comportamiento extraño se soluciona al definir la estructura localmente, p.

[StructLayout(LayoutKind.Sequential)] 
public struct Margins 
{ 
    public int Left; 
    public int Right; 
    public int Top; 
    public int Bottom; 
} 
+2

Este es un buen punto. También es bueno tener en cuenta que este problema solo se puede reproducir en un pequeño subconjunto de máquinas, por lo que es particularmente desagradable. –

+0

Hasta ahora lo he notado en una tarjeta gráfica Intel HD 520, así como en otras tarjetas Intel HD en otras computadoras portátiles. –

+0

Además, si convierte 'Margins' en' clase', entonces el problema vuelve. Debe estar relacionado con el hecho de que 'System.Drawing.Printing' también es una' clase'. –

0

¿Por qué no crear la sombra con el mismo objeto que su "ventana" pero más grande y detrás de ella.

<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> 

O si necesita una barra de título transparente, que pueda ser reemplazado por un Editar <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> 

: Acabo de notar PO quiere AllowsTransparency establece en False. No puedo ver que una sombra funcione sin que sea "Verdadera", por decirlo de alguna manera.