3
votes

When single-clicking a notification icon in Vista (such as the network or sound icons) you are presented with a bordered yet captionless dialog (http://i.msdn.microsoft.com/Aa511448.NotificationArea22(en-us,MSDN.10).png):

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

How can I emulate these in WPF? Creating a new window and setting WindowStyle to "None" and ResizeMode to "CanResize" produces a close result except that the frame is slightly too thin and the dialog is resizable, which is undesirable. Setting ResizeMode to "NoResize" results in a window with no Aero border (just a thin 2px solid line border.)

5

5 Answers

2
votes

The trick is to add the border your self. I did so by making the main content element a DockPanel and adding a Border. You can use the border to customize the look to match the Vista Style windows. I'm not good with colors so I can't name that particular one but used Gray as an example.

Try the following

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

I finally figured it out: If you set WindowStyle to "None" and ResizeMode to "CanResize" then you'll get the correct thick border without a caption, the only hitch is that you can still resize the window.

Fortunately this problem is easily rectified by handling WM_NCHITTEST for your Window instance:

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

By never letting Windows know that the cursor is on a border, we will never be presented with a resize cursor.

1
votes

What you need is to specify the right combination of window styles, WPF does not expose all the options available in Windows but you can set them yourself using pinvoke.

I'm not at a Vista machine right now so I can't test style combination to see what gives the correct look but the list of styles (in C#) is here http://pinvoke.net/default.aspx/user32/GetWindowLong.html

in you're Window class:

[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);
}
0
votes

I figured I'd post what I've come up with so far. This gets pretty close:

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

Clearly they are using more than just a transparency on the ActiveWindowBorderColor to draw the middle of the border. It seems that the top 1/4 has a white overlay while the bottom 3/4 has a black overlay. Also the outer border has an accent color on the right and bottom edges. If I were to do this for real I would create a UserControl that derives from Border to handle all of the little details like that (and allow me to resize if I want) and throw the Window's style into a resource dictionary.

0
votes

There is a solution in code posted here. I'm going to look into doing it in straight XAML though, there should be a way to style your window border so it looks close. You should also take a look at this for a better explanation of what the forum post is doing.