8

I have a window that does not have a title bar (WindowStyle == WindowStyle.None). The entire window uses the Aero glass effect. When I make the window unresizeable (ResizeMode == ResizeMode.NoResize), the glass effect disappears and my controls just hang in midair. (Essentially, the window itself disappears but leaves its contents.)

Is there a way for me to make the window unresizeable without getting rid of the window frame?


I have read the question Enable Vista glass effect on a borderless WPF window, but that's not quite what I want--I would like to keep the window border. For an example of what I would like my window to look like, hit Alt+Tab with Aero enabled.


To clarify, I do no want the resize cursors to show up at all when hovering over the window border. This is essentially what I want my window to look like:

Projector

The solution doesn't have to be strictly WPF--I am fine with hacking around with the Win32 API in order to achieve this.

Community
  • 1
  • 1
Sasha Chedygov
  • 127,549
  • 26
  • 102
  • 115

4 Answers4

13

You can hook the wndproc and intercept the WM_WINDOWPOSCHANGING message. Not strictly WPF, but probably your best bet.

If you want to hide the resize cursors, then your best bet is to intercept WM_NCHITTEST. Call the DefWindowProc (to get the default behavior), and test the return value; if it's HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTTOP, HTTOPLEFT, or HTTOPRIGHT, change the return value to HTBORDER.

Eric Brown
  • 13,774
  • 7
  • 30
  • 71
11

Based on Erics answer.

Example Image

public partial class MainWindow : Window
{
    [DllImport("DwmApi.dll")]
    public static extern int DwmExtendFrameIntoClientArea(
        IntPtr hwnd,
        ref MARGINS pMarInset);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr DefWindowProc(
        IntPtr hWnd,
        int msg,
        IntPtr wParam,
        IntPtr lParam);

    private const int WM_NCHITTEST = 0x0084;
    private const int HTBORDER = 18;
    private const int HTBOTTOM = 15;
    private const int HTBOTTOMLEFT = 16;
    private const int HTBOTTOMRIGHT = 17;
    private const int HTLEFT = 10;
    private const int HTRIGHT = 11;
    private const int HTTOP = 12;
    private const int HTTOPLEFT = 13;
    private const int HTTOPRIGHT = 14;

    public MainWindow()
    {
        InitializeComponent();

        this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        try
        {
            // Obtain the window handle for WPF application
            IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
            HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
            mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);
            mainWindowSrc.AddHook(WndProc);

            // Set Margins
            MARGINS margins = new MARGINS();
            margins.cxLeftWidth = 10;
            margins.cxRightWidth = 10;
            margins.cyBottomHeight = 10;
            margins.cyTopHeight = 10;

            int hr = DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
            //
            if (hr < 0)
            {
                //DwmExtendFrameIntoClientArea Failed
            }
        }
        // If not Vista, paint background white.
        catch (DllNotFoundException)
        {
            Application.Current.MainWindow.Background = Brushes.White;
        }
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // Override the window hit test
        // and if the cursor is over a resize border,
        // return a standard border result instead.
        if (msg == WM_NCHITTEST)
        {
            handled = true;
            var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
            switch (htLocation)
            {
                case HTBOTTOM:
                case HTBOTTOMLEFT:
                case HTBOTTOMRIGHT:
                case HTLEFT:
                case HTRIGHT:
                case HTTOP:
                case HTTOPLEFT:
                case HTTOPRIGHT:
                    htLocation = HTBORDER;
                    break;
            }

            return new IntPtr(htLocation);
        }

        return IntPtr.Zero;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct MARGINS
{
    public int cxLeftWidth;      // width of left border that retains its size
    public int cxRightWidth;     // width of right border that retains its size
    public int cyTopHeight;      // height of top border that retains its size
    public int cyBottomHeight;   // height of bottom border that retains its size
};

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="150" Width="200" 
    Background="Transparent"
    WindowStyle="None"
    ResizeMode="CanResize"
>
    <Grid Background="White" Margin="10,10,10,10">
        <Button Content="Go Away" Click="Button_Click" Height="20" Width="100" />
    </Grid>
</Window>
Cameron MacFarland
  • 70,676
  • 20
  • 104
  • 133
  • I'm going to give the answer to Eric to be fair, since he first posted the idea, but thank you for the working example. :) – Sasha Chedygov Aug 27 '10 at 21:42
  • 1
    That's why I mentioned it. I just wanted to see if I could get it working. – Cameron MacFarland Aug 27 '10 at 23:00
  • 1
    Shouldn't DefWindowProc be called in all cases though? In this code you only call the original function if the msg is WM_NCHITTEST. I'd imagine that to do the override correctly, any other message should simply return the result given by the original function. – Nyerguds Apr 26 '11 at 14:20
  • This doesn't quite work. A white border is added to the inner frame of the window. That's fine if you're using a white background, but otherwise it won't work. – Ed S. Jun 15 '11 at 23:49
  • 1
    @Ed S. In the MainWindow_Loaded event handler, change the margins on all sides from 10 to 12 will move the border to underneath the WPF window area, effectively hiding it. – Cameron MacFarland Jun 16 '11 at 02:52
1

One hackish way to do it would be to set the MinWidth/MaxWidth and MinHeight/MaxHeight properties to effectively make it unresizeable. Of course, the problem there is you'll still get the resize cursors over the borders.

RQDQ
  • 15,461
  • 2
  • 32
  • 59
1

Why don't you just create this window border for the window? It's using an offset to set the colors of the window. So, an easy way is just to wrap a whole border around your window and on top of that you get your own colors!

Kevin
  • 3,509
  • 4
  • 31
  • 45
  • An when I mean wrap it around a window, i mean wrap basically – Kevin Aug 02 '10 at 16:01
  • But then I can't make it match the user's current theme. I could make a custom border that looks exactly like the default Aero theme, but if the user has customized it (or is using a different theme), the window wouldn't match. – Sasha Chedygov Aug 02 '10 at 20:23
  • I'm not throwing this answer away, though, I may end up having to do this. – Sasha Chedygov Aug 02 '10 at 20:53
  • well... if you want customized colors. Why don't you just bind the offset and let the user's select there own colors? if you are using the MVVM approach, in your view model set a string with the color code and bind it into the offset, wouldn't that work? – Kevin Aug 03 '10 at 18:57
  • and if you want something to change based on key strokes. make a property that does this : public void OnKeyChanged (object sender, keyboardEventArgs e ) { if(e.Key = Tab && e.Key = Alt) { PropertyColor = "White" } else PropertyColor = Blue (but use color codes) } – Kevin Aug 03 '10 at 18:59
  • That's not my point. I want the window frame to match the user's current theme. So if they are using some custom black Aero theme like [this](http://browse.deviantart.com/customization/skins/windows7/visualstyle/#/d2vcxx3), I want the window to look like that as well. Making the user go through the trouble of creating their own window theme that doesn't even fully match their selected one is unacceptable, and having the window be hard-coded to use the default Aero style (as a compromise) is a sub-par solution (though I might have to resort to that). – Sasha Chedygov Aug 11 '10 at 21:53
  • Why can't you use WPF's built-in support? E.g., a DynamicResource to `SystemColors`? (http://msdn.microsoft.com/en-us/library/system.windows.systemcolors.aspx) – Greg D Aug 12 '10 at 16:38