Подтвердить что ты не робот

DropShadow для окна без полей WPF

У меня есть окно WPF с установкой WindowStyle на none. Есть ли способ заставить это окно удалить тень (например, тот, который вы получаете, когда WindowStyle не является ничем)? Я не хочу, чтобы AllowTransparency был прав, потому что это влияет на производительность. И я также не хочу отключать аппаратное рендеринг (я где-то читал, что прозрачность работает лучше с отключенным).

4b9b3361

Ответ 1

Я написал небольшой класс утилиты, который способен делать именно то, что вы хотите: снимите стандартную тень над безграничным Window, но с AllowsTransparency установите на false.

Вам просто нужно вызвать метод DropShadowToWindow(Window window). Желательно, чтобы вы совершили этот вызов сразу после конструктора окна InitializeComponent(), но он будет работать, даже если вы вызываете его после отображения окна.

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

Ответ 3

Ответ Patrick отлично работает, за исключением случаев, когда размещено окно win32. Когда это произойдет, вы заметите, что размещенное окно "вымыто" (похоже, что окна применяют эффект "стеклянного листа" ко всему размещенному окну). Это нечетное поведение фиксируется при локальном определении структуры, например

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

Ответ 4

Если вы разрешаете изменять размер границ окна, установив ResizeMode на CanResize, вы получите теневую копию ОС. Затем вы можете установить значения MaxWidth, MinWidth, MaxHeight и MinHeight, чтобы предотвратить изменение размера.

Если у вас есть окно без рамки без стиля, вам нужно будет предоставить весь вид окна в вашем собственном визуальном дереве, включая тень, поскольку эта комбинация настроек совпадает с тем, что вы не хотите что обеспечивает ОС.

EDIT:

С этой точки, если ваш размер окна исправлен, просто добавьте dropshadow, возможно, как <Rectangle/> в качестве первого элемента в содержимом <Canvas/>

что-то вроде этого:

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

Обратите внимание, что свойство Fill этого первого Rectangle является частично прозрачным, что вы также можете использовать с Opacity свойством Rectangle. Вы можете использовать графику вашей собственной или другой формы, чтобы настроить внешний вид тени.

Обратите внимание, что это нарушает ваше требование иметь AllowsTransparency be False, но у вас нет выбора: если вы хотите прозрачность, вы должны это разрешить.

Ответ 5

Почему бы просто не создать тень с тем же объектом, что и ваше "окно", но больше и позади него.

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

Или, если вам нужна прозрачная строка заголовка, ее можно заменить на <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>

Изменить: я просто заметил, что OP хочет, чтобы AllowsTransparency установили значение False. Я не вижу тень, чтобы работать без того, чтобы она была "Истина", т.).