Эмуляция диалоговых окон значков уведомлений Vista с помощью WPF
-
21-08-2019 - |
Вопрос
При одиночном щелчке на значке уведомления в 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, чтобы обрабатывать все подобные мелкие детали (и позволять мне изменять размер, если я захочу) и помещать стиль окна в словарь ресурсов.