DropShadow dla okna bezramkowego WPF

Mam okno WPF z WindowStyle ustawionym na none. Czy jest jakiś sposób, aby zmusić to okno do rzucenia cienia (jak ten, który dostajesz, gdy WindowStyle nie jest żaden)? Nie chcę ustawiać AllowTransparency na true, ponieważ wpływa to na wydajność. A ja też nie chcę wyłączać renderowania sprzętowego (gdzieś czytałem, że przezroczystość działa lepiej przy wyłączonym).

Author: TripShock, 2010-07-30

5 answers

Napisałem małą klasę użyteczności, która jest w stanie zrobić dokładnie to, co chcesz: zrzucić standardowy cień na bezramkowy Window, ale mając AllowsTransparency ustawione na false.

Wystarczy wywołać metodę DropShadowToWindow(Window window). Preferowane jest wykonanie tego wywołania zaraz po wywołaniu konstruktora InitializeComponent() okna, ale będzie ono działać nawet jeśli wywołasz je po wyświetleniu okna.

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;
        }
    }
}
 31
Author: cprcrack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-16 13:30:03

Użyj Microsoft WPF Shell Integration Library , łatwiejsze i z lepszą wydajnością

 5
Author: Eduardo Molteni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-15 00:17:33

ODPOWIEDŹ Patryka działa świetnie, z wyjątkiem sytuacji, gdy hostowane jest okno win32. Gdy tak się stanie, zauważysz, że okno hostowane jest "wypłukane" (wygląda na to, że windows stosuje efekt "szklanego arkusza" do całego hostowanego okna). To dziwne zachowanie jest stałe podczas definiowania struktury lokalnie, np.

[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
    public int Left;
    public int Right;
    public int Top;
    public int Bottom;
}  
 5
Author: Omer Ran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-14 16:18:47

Jeśli zezwolisz na zmianę rozmiaru okna, ustawiając ResizeMode na CanResize, otrzymasz Cień systemu operacyjnego. Następnie można ustawić MaxWidth, MinWidth, MaxHeight, i MinHeight do wartości, które zapobiegną zmianie rozmiaru.

Jeśli masz okno bez obramowania bez stylu, będziesz musiał podać cały wygląd okna we własnym drzewie wizualnym, w tym cień, ponieważ ta kombinacja ustawień jest taka sama, jak stwierdzenie, że nie chcesz tego, co zapewnia system operacyjny.

EDIT:

Od tego momentu, Jeśli rozmiar okna jest stały, po prostu dodaj dropshadow, być może jako <Rectangle/> jako pierwszy element w zawartości <Canvas/>

Coś takiego:

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

Zauważ, że Fill właściwość tego pierwszego Rectangle jest częściowo przezroczysta, co można również zrobić z Opacity właściwością Rectangle. Możesz użyć grafiki własnej lub innego kształtu, aby dostosować wygląd cienia.

Zauważ, że narusza to twój wymóg posiadania AllowsTransparency be False, ale nie masz wyboru: jeśli chcesz przejrzystości, musisz na to pozwolić.

 3
Author: Rob Perkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-04 19:21:43

Dlaczego po prostu nie stworzyć cienia z tego samego obiektu co" okno", ale większy i za nim.

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

Lub jeśli potrzebujesz przezroczystego paska tytułu, można go zastąpić <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>

Edit: właśnie zauważyłem, że OP chce ustawić AllowsTransparency Na False. Nie widzę cienia, który mógłby działać bez tego, że jest "prawdziwy", thouth.

 0
Author: Carol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-23 21:06:47