web-dev-qa-db-ja.com

シャドウVS2012スタイルのWPFボーダレスウィンドウ

Visual Studio 2012のようなアプリケーションを作成しようとしています。ウィンドウの境界線を削除するために WindowChrome を使用し、xamlの境界線の色を変更しました。

方法がわからないのは、ウィンドウの影をペイントすることです。ここで、私が言っていることのスクリーンショットを見ることができます。

Visual Studio Borderless window with shadow

ご覧のとおり、影があり、その色は境界線の色でもあります

WPFを使用して実装する方法を知っていますか?

21
Daniel Peñalba

更新(10月'17)

もう4年が経ちましたが、これに再び取り組むことに興味があり、MahApps.Metroをもう一度いじっていました it 。私のModernChromeライブラリは、Visual Studio 2017のようなカスタムウィンドウを提供します。

ModernChrome Sample

輝く境界線に関する部分にのみ関心がある可能性が高いため、MahApps.Metro自体を使用するか、アタッチするクラスGlowWindowBehaviorの作成方法を確認する必要があります。カスタムModernWindowクラスに境界線をグローします。 MahApps.Metroのいくつかの内部と、2つの依存関係プロパティGlowBrushおよびNonActiveGlowBrushに大きく依存しています。

輝く境界線のみをカスタムアプリケーションに含める場合は、MahApps.Metroを参照して、_GlowWindowBehavior.csそして、カスタムウィンドウクラスを作成し、それに応じて参照を調整します。これはせいぜい15分です。

この質問と私の回答は非常に頻繁にアクセスされているので、私の最新の適切なソリューションが役立つことを願っています:)


元の投稿(13年2月)

Visual Studio 2012のユーザーインターフェイスをコピーするために、このようなライブラリに取り組んできました。カスタムのchromeはそれほど難しくありませんが、注意が必要なのは、実装が難しいこの輝く境界線です。ウィンドウの背景色を透明に設定し、メイングリッドのパディングを約30ピクセルにします。グリッドの周りの境界線に色を付け、色付きのシャドウエフェクトに関連付けることができますが、このアプローチではAllowsTransparencyをtrueに設定する必要があります。絶対にやりたくないこと!

私の現在のアプローチは、境界線に色付きの影の効果があり、透明であるがコンテンツがまったくないようなウィンドウを作成します。メインウィンドウの位置が常に変化する境界線を保持するウィンドウの位置を更新するだけです。そのため、最終的には、境界線がメインウィンドウの一部であるという偽のメッセージを含む2つのウィンドウを処理しています。これは、DWMライブラリがウィンドウに色付きのドロップシャドウ効果を与える方法を提供しないために必要でした。VisualStudio 2012は、私が試したように似ていると思います。

そして、この投稿をより多くの情報で拡張するために:Office 2013はそれを異なる方法で行います。ウィンドウの周囲の境界線は1ピクセルの太さで色付けされていますが、シャドウはDWMによって this one のようなコードで描かれています。青/紫/緑の境界線と通常の境界線なしで生活できる場合、これが私が選択するアプローチです! AllowsTransparencyをtrueに設定しないでください。そうしないと失われます。

そして、これは私のウィンドウのスクリーンショットであり、奇妙な色でそれがどのように見えるかを強調しています:

Metro UI


ここに開始方法のヒントがあります

私のコードは非常に長いので、基本的なことだけをお見せできるようになり、少なくとも何らかの形で開始できるはずです。まず最初に、何らかの方法で(手動またはMahApps.Metro昨日試したパッケージ-ソースコードにいくつか修正を加えて、これは本当に良い(1))そして現在、輝く影の境界線の実装に取り​​組んでいます。これを今後GlowWindowと呼びます。最も簡単な方法は、次のXAMLコードでウィンドウを作成することです

<Window x:Class="MetroUI.Views.GlowWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    x:Name="GlowWindow"
    Title="" Width="300" Height="100" WindowStartupLocation="Manual"
    AllowsTransparency="True" Background="Transparent" WindowStyle="None"
    ShowInTaskbar="False" Foreground="#007acc" MaxWidth="5000" MaxHeight="5000">
    <Border x:Name="OuterGlow" Margin="10" Background="Transparent"
            BorderBrush="{Binding Foreground, ElementName=GlowWindow}"
            BorderThickness="5">
        <Border.Effect>
            <BlurEffect KernelType="Gaussian" Radius="15" RenderingBias="Quality" />
        </Border.Effect>
    </Border>
</Window>

結果のウィンドウは、次の図のようになります。

GlowWindow

次の手順は非常に困難です。メインウィンドウが表示されたら、GlowWindowをメインウィンドウの背後に表示し、メインウィンドウを移動またはサイズ変更するときにGlowWindowの位置を更新する必要があります。 ANDが発生する可能性のある視覚的なグリッチを防ぐために私が提案するのは、ウィンドウの位置またはサイズの変更のたびにGlowWindowを非表示にすることです。このようなアクションが終了したら、もう一度表示します。

私はさまざまな状況で呼び出されるいくつかのメソッドを持っています(それはたくさんあるかもしれませんが、念のために)

private void UpdateGlowWindow(bool isActivated = false) {
    if(this.DisableComposite || this.IsMaximized) {
        this.glowWindow.Visibility = System.Windows.Visibility.Collapsed;
        return;
    }
    try {
        this.glowWindow.Left = this.Left - 10;
        this.glowWindow.Top = this.Top - 10;
        this.glowWindow.Width = this.Width + 20;
        this.glowWindow.Height = this.Height + 20;
        this.glowWindow.Visibility = System.Windows.Visibility.Visible;
        if(!isActivated)
            this.glowWindow.Activate();
    } catch(Exception) {
    }
}

このメソッドは、メインウィンドウにアタッチしたカスタムWndProcで主に呼び出されます。

/// <summary>
/// An application-defined function that processes messages sent to a window. The WNDPROC type
/// defines a pointer to this callback function.
/// </summary>
/// <param name="hwnd">A handle to the window.</param>
/// <param name="uMsg">The message.</param>
/// <param name="wParam">Additional message information. The contents of this parameter depend on
/// the value of the uMsg parameter.</param>
/// <param name="lParam">Additional message information. The contents of this parameter depend on
/// the value of the uMsg parameter.</param>
/// <param name="handled">Reference to boolean value which indicates whether a message was handled.
/// </param>
/// <returns>The return value is the result of the message processing and depends on the message sent.
/// </returns>
private IntPtr WindowProc(IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr lParam, ref bool handled) {
    // BEGIN UNMANAGED WIN32
    switch((WinRT.Message)uMsg) {
        case WinRT.Message.WM_SIZE:
            switch((WinRT.Size)wParam) {
                case WinRT.Size.SIZE_MAXIMIZED:
                    this.Left = this.Top = 0;
                    if(!this.IsMaximized)
                        this.IsMaximized = true;
                    this.UpdateChrome();
                    break;
                case WinRT.Size.SIZE_RESTORED:
                    if(this.IsMaximized)
                        this.IsMaximized = false;
                    this.UpdateChrome();
                    break;
            }
            break;

        case WinRT.Message.WM_WINDOWPOSCHANGING:
            WinRT.WINDOWPOS windowPosition = (WinRT.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WinRT.WINDOWPOS));
            Window handledWindow = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
            if(handledWindow == null)
                return IntPtr.Zero;
            bool hasChangedPosition = false;
            if(this.IsMaximized == true && (this.Left != 0 || this.Top != 0)) {
                windowPosition.x = windowPosition.y = 0;
                windowPosition.cx = (int)SystemParameters.WorkArea.Width;
                windowPosition.cy = (int)SystemParameters.WorkArea.Height;
                hasChangedPosition = true;
                this.UpdateChrome();
                this.UpdateGlowWindow();
            }
            if(!hasChangedPosition)
                return IntPtr.Zero;
            Marshal.StructureToPtr(windowPosition, lParam, true);
            handled = true;
            break;
    }
    return IntPtr.Zero;
    // END UNMANAGED WIN32
}

ただし、まだ問題が残っています。メインウィンドウのサイズを変更すると、GlowWindowはそのサイズでウィンドウ全体をカバーできなくなります。メインウィンドウのサイズを画面のMaxWidth程度に変更すると、GlowWindowのwidtは10のマージンを追加したのと同じ値+ 20になります。したがって、右端はメインウィンドウの右端の直前で中断され、見苦しくなります。これを防ぐために、フックを使用してGlowWindowをツールウィンドウにしました。

this.Loaded += delegate {
    WindowInteropHelper wndHelper = new WindowInteropHelper(this);
    int exStyle = (int)WinRT.GetWindowLong(wndHelper.Handle, (int)WinRT.GetWindowLongFields.GWL_EXSTYLE);
    exStyle |= (int)WinRT.ExtendedWindowStyles.WS_EX_TOOLWINDOW;
    WinRT.SetWindowLong(wndHelper.Handle, (int)WinRT.GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
};

それでも、いくつかの問題が発生します。マウスをGlowWindowの上に置いて左クリックすると、GlowWindowがアクティブになり、フォーカスを取得します。つまり、次のようなメインウィンドウに重なります。

Overlapping GlowWindow

それを防ぐには、境界線のActivatedイベントをキャッチして、メインウィンドウを前面に表示します。

これをどのように行うべきですか?

私はこれを試さないことをお勧めします-私が望んでいたものを達成するのに約1ヶ月かかりましたが、Office 2013のようなアプローチに行くためにいくつかの問題があります-色付きの境界線とDWM API呼び出しによる通常の影-他には何もありませんが、それでも見栄えは良いです。

Office 2013


(1) ウィンドウ8で無効になっているウィンドウの周囲の境界線を有効にするために、いくつかのファイルを編集しました。さらに、タイトルバーのPaddingを操作して、その場所にスクイーズされているように見えないようにし、最後にVisual Studioのタイトルのレンダリング方法を模倣するようにAll-Capsプロパティを変更しました。これまでのMahApps.Metroは、通常のP/Invoke呼び出しでは実装できなかったAeroSnapをサポートしているため、メインウィンドウを描画するより良い方法です。

33

この単純なxamlコードを使用できます

<Window x:Class="VS2012.MainWindow" 
         xmlns=http://schemas.Microsoft.com/winfx/2006/xaml/presentation 
         xmlns:x=http://schemas.Microsoft.com/winfx/2006/xaml 
         Title="MainWindow" 
         Height="100" Width="200" 
         AllowsTransparency="True" WindowStyle="None" Background="Transparent"> 
<Border BorderBrush="DarkOrange" BorderThickness="1" Background="White" Margin="5">
         <Border.Effect>
                <DropShadowEffect ShadowDepth="0" BlurRadius="5" Color="DarkOrange"/>
         </Border.Effect>
</Border>
</Window> 
3
Hady Mahmoodi

これは「メトロスタイル」(Windows 8スタイル)と呼ばれます。 this Code Projectの記事はあなたにとって興味深いものであり、あなたの役に立つと思います。

Elysium を試すことができます。これは、MITライセンスおよび含まれるApplicationBarクラスとToastNotificationクラスの下でライセンスされています。または MetroToolKit 、codeplextからも。

This はElysiumについての素晴らしいチュートリアルであり、あなたに役立つと思います。

シャドウの場合、XAMLでBitmapEffectからBorderGridを追加するだけです:

<Grid>
    <Border BorderBrush="#FF006900" BorderThickness="3" Height="157" HorizontalAlignment="Left" Margin="12,12,0,0" Name="border1" VerticalAlignment="Top" Width="479" Background="#FFCEFFE1" CornerRadius="20, 20, 20, 20">
        <Border.BitmapEffect>
          <DropShadowBitmapEffect Color="Black" Direction="320" ShadowDepth="10" Opacity="0.5" Softness="5" />
        </Border.BitmapEffect>
        <TextBlock Height="179" Name="textBlock1" Text="Hello, this is a beautiful DropShadow WPF Window Example." FontSize="40" TextWrapping="Wrap" TextAlignment="Center" Foreground="#FF245829" />
    </Border>
</Grid>

enter image description here

3
Ionică Bizău

私は同じ効果を得ようとしています、私のアプリは.NET 4を使用しているため、WindowChromeを直接使用できません(したがって、 Microsoft Windows Shell ライブラリを使用して取得します同じ)。

この thread では、spy ++を使用すると、Visual StudioにVisualStudioGlowWindowと呼ばれる4つのウィンドウがあることがわかります輝く効果。 AllowsTransparencyプロパティをtrueに設定するとパフォーマンスがどのように低下​​するかは、すでに多くの場所で説明されています。

だから、私はVSの方法を試みましたが、結果は悪くありません(少なくとも、私にとって);メインウィンドウでぼかしや同様の効果を使用する必要はありません。いくつかのウィンドウの状態(フォーカス/表示/非表示)で少し戦わなければなりませんでした。

github に必要なものをすべて配置しました-これが役立つことを願っています。

1
SiMoStro