web-dev-qa-db-ja.com

DropShadow for WPFボーダーレスウィンドウ

WindowStyleをnoneに設定したWPFウィンドウがあります。このウィンドウにシャドウを強制的にドロップさせる方法はありますか(WindowStyleがnoneでない場合に得られるような)。 AllowTransparencyをtrueに設定したくないのは、パフォーマンスに影響するためです。また、ハードウェアレンダリングを無効にしたくありません(透明度を無効にするとパフォーマンスが向上することをどこかで読みました)。

24
TripShock

私はあなたが望むことを正確に実行できる小さなユーティリティクラスを書きました:ボーダーレスWindowに標準の影を落としますが、AllowsTransparencyfalseに設定します。

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

Win32ウィンドウがホストされている場合を除いて、Patrickの回答はうまく機能します。これが発生すると、ホストされたウィンドウが「ウォッシュアウト」されていることがわかります(ウィンドウがホストされたウィンドウ全体に「ガラス板」効果を適用しているように見えます)。この奇妙な動作は、構造をローカルに定義するときに修正されます。

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

ResizeModeCanResizeに設定して、ウィンドウの境界線のサイズ変更を許可すると、OSのドロップシャドウが表示されます。次に、MaxWidthMinWidthMaxHeight、およびMinHeightを、サイズ変更を防止する値に設定できます。

スタイルのないボーダーレスウィンドウがある場合、ドロップシャドウを含む独自のビジュアルツリー内のウィンドウのすべての外観を提供する必要があります。これは、この設定の組み合わせが、OSに必要なものではないことを示すのと同じであるためです。提供します。

編集:

その時点から、ウィンドウサイズが固定されている場合は、<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>

最初のFillRectangleプロパティは部分的に透過的であることに注意してください。これは、OpacityRectangleプロパティでも実行できます。独自のグラフィックや別の形状のグラフィックを使用して、ドロップシャドウの外観をカスタマイズできます。

これは、AllowsTransparencyFalseにするという要件に違反していますが、選択の余地はありません。透明性が必要な場合は、許可する必要があります。

3
Rob Perkins

「ウィンドウ」と同じオブジェクトで影を作成するだけでなく、その後ろに大きくします。

<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に設定したいことに気づきました。それが「本当」でなければ、私は働く影を見ることができません。

0
Carol