21

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

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

影付きのVisualStudioボーダレスウィンドウ

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

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

4

5 に答える 5

33

更新 (2017 年 10 月)

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

ModernChrome サンプル

グローイングボーダーに関する部分のみに関心がある可能性が高いため、MahApps.Metro自体を使用するかGlowWindowBehavior、カスタム クラスにグロー ボーダーをアタッチするクラスを作成する方法を参照してくださいModernWindowこれは、 MahApps.Metroの一部の内部構造と 2 つの依存プロパティGlowBrushおよびに大きく依存していますNonActiveGlowBrush

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

この質問と私の回答は非常に頻繁にアクセスされているため、最新の適切な解決策が役立つことを願っています:)


元の投稿 (2 月 '13)

Visual Studio 2012 のユーザー インターフェイスをコピーするライブラリの作成に取り組んでいます。カスタム クロームはそれほど難しくありませんが、実装が難しいこの光る境界線に注意する必要があります。ウィンドウの背景色を透明に設定し、メイン グリッドのパディングを約 30px に設定すると言うことができます。グリッドの周囲の境界線に色を付けて、色付きの影の効果に関連付けることができますが、このアプローチAllowsTransparencyでは true に設定する必要があり、アプリケーションの視覚的パフォーマンスが大幅に低下します。これは絶対にやりたくないことです!

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

さらに詳しい情報を追加してこの投稿を拡張するには、Office 2013 ではこれが異なります。ウィンドウの周囲の境界線はわずか 1 ピクセルの太さで色付きですが、影は DWM によって次のようなコードで描画されます。青/紫/緑の境界線がなくても、通常の境界線だけで生活できる場合、これが私が選択するアプローチです! AllowsTransparencytrue に設定しないでください。そうしないと、負けてしまいます。

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

メトロ 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 を非表示にすることです。そのようなアクションが終了したら、もう一度表示します。

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

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 の幅は、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 がアクティブになり、フォーカスが得られます。つまり、メイン ウィンドウが次のように重なって表示されます。

GlowWindow のオーバーラップ

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

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

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

オフィス 2013


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

于 2013-02-09T17:08:38.393 に答える
5

この単純な 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> 
于 2013-11-16T03:05:52.560 に答える
3

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

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

これは Elysium に関する素晴らしいチュートリアルです。役に立つと思います。

シャドウの場合は、XAML でaBitmapEffectに a をBorder追加するだけです。Grid

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

ここに画像の説明を入力

于 2013-02-06T13:37:44.973 に答える