Эмуляция диалоговых окон значков уведомлений Vista с помощью WPF

StackOverflow https://stackoverflow.com/questions/671012

Вопрос

При одиночном щелчке на значке уведомления в Vista (например, на значках сети или звука) отображается диалоговое окно с рамками, но без надписей (http://i.msdn.microsoft.com/Aa511448.NotificationArea22 (ru-us,MSDN.10).png):

http://i.msdn.microsoft.com/Aa511448.NotificationArea22 (ru-us,MSDN.10).png

Как я могу эмулировать их в WPF?Создание нового окна и установка WindowStyle на "None" и ResizeMode на "CanResize" дают близкий результат, за исключением того, что рамка немного слишком тонкая, а размер диалогового окна можно изменять, что нежелательно.Установка ResizeMode в значение "NoResize" приводит к появлению окна без аэро-границы (только тонкая сплошная линия размером 2 пикселя).)

Это было полезно?

Решение 2

Я наконец-то понял это:Если вы установите WindowStyle в "None", а ResizeMode в "CanResize", то вы получите правильную толстую рамку без подписи, единственная загвоздка в том, что вы все еще можете изменить размер окна.

К счастью, эта проблема легко устраняется путем обработки WM_NCHITTEST для вашего Window экземпляр:

private IntPtr _hwnd;

protected override void OnSourceInitialized(EventArgs e) {
    _hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
    System.Windows.Interop.HwndSource.FromHwnd(_hwnd).AddHook(_WndProc);
    base.OnSourceInitialized(e);
}

private const int WM_NCHITTEST = 132;
private const int HTCLIENT = 1;

private IntPtr _WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
    // We should only receive messages for our own window handle.
    System.Diagnostics.Debug.Assert(hwnd == _hwnd);

    if (msg == WM_NCHITTEST) {
        handled = true;
        return (IntPtr)HTCLIENT;
    }
    return IntPtr.Zero;
}

Если мы никогда не сообщим Windows, что курсор находится на границе, нам никогда не будет представлен курсор изменения размера.

Другие советы

Хитрость в том, чтобы добавить границу самостоятельно.Я сделал это, сделав основной элемент содержимого DockPanel и добавив Границу.Вы можете использовать рамку, чтобы настроить внешний вид в соответствии с окнами в стиле Vista.Я плохо разбираюсь в цветах, поэтому не могу назвать этот конкретный, но в качестве примера использовал Серый.

Попробуйте следующее

<Window x:Class="WpfApplication10.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" 
    Height="300" 
    Width="300"
    WindowStyle="None"
    ResizeMode="NoResize">
    <DockPanel>
        <Border
            BorderBrush="Gray"
            BorderThickness="5">

            <TextBlock>Here we go</TextBlock>

        </Border>
    </DockPanel>
</Window>

Что вам нужно, так это указать правильную комбинацию стилей окон, WPF не предоставляет все параметры, доступные в Windows, но вы можете настроить их самостоятельно с помощью pinvoke.

Я сейчас не на компьютере с Vista, поэтому я не могу протестировать комбинацию стилей, чтобы увидеть, что дает правильный вид, но список стилей (на C #) находится здесь http://pinvoke.net/default.aspx/user32/GetWindowLong.html

в классе "твое окно":

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);

[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

private const int GWL_STYLE = -16;
private const int GWL_EXSTYLE = -20;
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 SWP_NOZORDER = 0x0004;
private const UInt32 SWP_NOREDRAW = 0x0008;
private const UInt32 SWP_NOACTIVATE = 0x0010;
private const UInt32 SWP_FRAMECHANGED = 0x0020;

public override void OnSourceInitialized(EventArgs e)
{
    IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;

    // set styles to a combination of WS_ flags and exstyles to a combination of WS_EX_ flags

    SetWindowLong(hwnd, GWL_STYLE, styles);
    SetWindowLong(hwnd, GWL_EXSTYLE, exstyles);

    // and to activate changes:
    SetWindowPos(hwnd,IntPtr.Zero,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
}

Я решил опубликовать то, что у меня получилось на данный момент.Это уже довольно близко:

<Window.Style>
    <Style TargetType="{x:Type Window}">
        <Setter Property="AllowsTransparency"       Value="True"            />
        <Setter Property="Background"           Value="{x:Null}"        />
        <Setter Property="BorderBrush"          Value="{x:Null}"        />
        <Setter Property="BorderThickness"      Value="0"           />
        <Setter Property="OverridesDefaultStyle"    Value="True"            />
        <Setter Property="ResizeMode"           Value="NoResize"        />
        <Setter Property="SizeToContent"        Value="WidthAndHeight"      />
        <Setter Property="WindowStyle"          Value="None"            />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Border BorderThickness="1" CornerRadius="4" Background="{x:Null}">
                        <Border.BorderBrush>
                            <SolidColorBrush Color="{x:Static SystemColors.WindowFrameColor}" Opacity="0.75" />
                        </Border.BorderBrush>
                        <Border BorderThickness="5" Background="{x:Null}">
                            <Border.BorderBrush>
                                <SolidColorBrush Color="{x:Static SystemColors.ActiveBorderColor}" Opacity="0.5" />
                            </Border.BorderBrush>
                            <Border BorderThickness="1" Background="White">
                                <Border.BorderBrush>
                                    <SolidColorBrush Color="{x:Static SystemColors.WindowFrameColor}" Opacity="0.75" />
                                </Border.BorderBrush>

                                <ContentPresenter />
                            </Border>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Style>

Очевидно, что они используют нечто большее, чем просто прозрачность ActiveWindowBorderColor, чтобы нарисовать середину границы.Кажется, что верхняя 1/4 имеет белое наложение, в то время как нижняя 3/4 имеет черное наложение.Также внешняя окантовка имеет акцентный цвет по правому и нижнему краям.Если бы я должен был сделать это по-настоящему, я бы создал UserControl, производный от Border, чтобы обрабатывать все подобные мелкие детали (и позволять мне изменять размер, если я захочу) и помещать стиль окна в словарь ресурсов.

В опубликованном коде есть решение здесь.Я собираюсь рассмотреть возможность сделать это в обычном XAML, хотя должен быть способ стилизовать границу вашего окна, чтобы оно выглядело близко.Вам также следует взглянуть на это для лучшего объяснения того, что делает сообщение на форуме.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top