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

Окно без полей WPF с тенью VS2012

Я пытаюсь создать приложение, которое выглядит как Visual Studio 2012. Я использовал WindowChrome, чтобы удалить границы окна, и изменил цвет границы в моем xaml.

То, что я не знаю, как рисовать тень окна, здесь вы можете увидеть снимок экрана, что я говорю:

Visual Studio Borderless window with shadow

Как вы видите, есть тень, а цвет - также цвет рамки

Вы знаете, как реализовать его с помощью WPF?

4b9b3361

Ответ 1

Обновление (октябрь '17)

Прошло уже четыре года, и мне было интересно снова заняться этим, и поэтому я снова возился с MahApps.Metro и получил мои собственные библиотеки на его основе. Моя библиотека ModernChrome предоставляет настраиваемое окно, которое выглядит как Visual Studio 2017:

ModernChrome Sample

Поскольку вы, скорее всего, интересуетесь только частью светящейся границы, вы должны либо использовать сам MahApps.Metro, либо посмотреть, как я создал класс GlowWindowBehavior, который привязывает границы свечения к моему пользовательскому классу ModernWindow. Он зависит от некоторых внутренних элементов MahApps.Metro и двух свойств зависимостей GlowBrush и NonActiveGlowBrush.

Если вы хотите включить только светящиеся границы в свои пользовательские приложения, просто укажите MahApps.Metro и скопируйте мой GlowWindowBehavior.cs и создайте собственный класс окон и соответствующим образом скопируйте ссылки. Это всего лишь 15 минут.

Этот вопрос и мой ответ были доступны очень часто, поэтому я надеюсь, что вы найдете мое новое подходящее решение полезным:)


Оригинальное сообщение (февраль '13)

Я работаю над такой библиотекой, чтобы скопировать пользовательский интерфейс Visual Studio 2012. Пользовательский хром не так уж и сложный, но то, что вам следует позаботиться, - это светящаяся граница, которую трудно реализовать. Вы могли бы просто сказать, чтобы цвет фона вашего окна был прозрачным и установил прокладку основной сетки примерно на 30 пикселей. Граница вокруг сетки может быть окрашена и связана с эффектом цветной тени, но этот подход заставляет вас установить AllowsTransparency значение true, которое резко снижает визуальную производительность вашего приложения, и это то, что вы определенно не хотите делать!

Мой нынешний подход к созданию такого окна, который имеет только цветной эффект тени на границе и прозрачен, но не имеет содержимого вообще. Evertime меняет положение моего главного окна. Я просто обновляю позицию окна, которое удерживает границу. Поэтому в конце я обрабатываю два окна с сообщениями о том, что граница будет частью главного окна. Это было необходимо, потому что библиотека DWM не предоставляет способ иметь цветной эффект тени для окон, и я думаю, что Visual Studio 2012 делает это похожее, как я пытался.

И чтобы расширить этот пост с дополнительной информацией: Office 2013 делает это по-другому. Граница вокруг окна толщиной всего 1px и цветная, но тень рисуется DWM с таким кодом, как здесь. Если вы можете жить без голубых/фиолетовых/зеленых границ и просто обычных, это подход, который я бы выбрал! Просто не устанавливайте 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, когда основное окно перемещается или изменяется. То, что я предлагаю предотвращать визуальные сбои, которые могут И произойти, заключается в том, чтобы скрыть 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 не сможет охватить все окно с его размером. То есть, если вы измените размер основного окна на максимальную ширину экрана, тогда ширина GlowWindow будет равна тому же значению + 20, так как я добавил к нему 10-кратное значение. Поэтому правый край будет прерван прямо перед правым краем главного окна, которое выглядит уродливым. Чтобы предотвратить это, я использовал крючок, чтобы сделать 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 и щелкните левой кнопкой мыши, он будет активирован и получит фокус, что означает, что он будет перекрывать главное окно, которое выглядит следующим образом:

Overlapping GlowWindow

Чтобы предотвратить простое перехват события Activated на границе и вывести главное окно на передний план.

Как вы должны это делать?

Я предлагаю НЕ попробовать это - мне потребовался месяц, чтобы достичь того, чего я хотел, и все же у него есть некоторые проблемы, поэтому я бы пошел на такой подход, как Office 2013, - цветная рамка и обычная тень с DWM API-вызовы - ничего другого, и все же он выглядит хорошо.

Office 2013


(1) Я только что редактировал некоторые файлы, чтобы включить рамку вокруг окна, которое отключено для Window 8 для меня. Кроме того, я манипулировал Padding в строке заголовка таким образом, что он не выглядит так, что sqiedzed inplace, и, наконец, я изменил свойство All-Caps, чтобы имитировать способ визуализации Visual Studio. Пока MahApps.Metro - лучший способ рисовать главное окно, поскольку он даже поддерживает AeroSnap, я не мог реализовать обычные вызовы P/Invoke.

Ответ 2

Это называется "Стиль Metro" (стиль Windows 8). Я думаю, что this Код Project интересен для вас, и это поможет вам.

Вы можете попробовать Elysium, который лицензируется по лицензии MIT и включает классы ApplicationBar и ToastNotification или MetroToolKit, также из кода.

Это - отличный учебник по Elysium, я думаю, что он вам помогает.

Для тени просто добавьте BitmapEffect в Border из Grid в XAML:

<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

Вы можете использовать этот простой код 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> 

Ответ 4

Я пытаюсь получить тот же эффект, мое приложение использует .NET 4, и поэтому я не могу напрямую использовать WindowChrome (так что я использую Microsoft Windows Shell, чтобы получить то же самое).

В этом потоке правильно отмечено, что с помощью spy ++ можно увидеть, что Visual Studio имеет четыре окна под названием VisualStudioGlowWindow для реализации сияющего эффекта. Во многих местах уже описано, как свойство AllowsTransparency true может уменьшить производительность.

Итак, я попытался пройти VS-образ, и результат неплохой (по крайней мере, для меня); не нужно использовать размытие или аналогичный эффект в главном окне, мне просто пришлось немного сражаться с некоторыми состояниями окна (фокус/видимый/скрытый).

Я поместил все необходимые вещи в github - надеюсь, что это может помочь.