Tag Archives: WPF

Exiting a WPF app

So this is a developing story. I’m working on a WPF app again these days and how do you exit a WPF when you’re done running? Normally a user or your code might close the main/last window and the app would exit, but what else can you do?

  • Application.Current.Shutdown() is what I’ve been trying and it wouldn’t work. Why? It turns out – it needs to be invoked on the dispatcher thread… ugh. Anyways – it seems to be THE WPF way, so I’m sticking with it:
    Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
  • Environment.Exit(0) should work WPF or not.
  • Process.GetCurrentProcess().Kill() is a rather sudden, but sure way to kill your own process.
Advertisements
Tagged ,

WPF/Silverlight vs. Jupiter Quirks – Opacity

Have you noticed the difference in how the Opacity property is handled by child elements the Windows 8 XAML vs. Silverlight or WPF?

Silverlight/WPF:

Windows 8 XAML (Jupiter):

Continue reading

Tagged , , , , ,

Asynchronous UI development in WinRT, Silverlight, Windows Phone & WPF with async/await keywords of C# 5.0

This is a reblog of my article from http://labs.vectorform.com

C# 5.0 comes with the new async/await keywords that make asynchronous code easier to write, read and maintain. This is very nice if you have properly declared methods that support this pattern and Windows Runtime (or WinRT – the API for Windows 8 Metro Style Apps) comes with a lot of these methods for long running tasks or ones of nondeterministic duration – especially in I/O or web calls. It is however completely lacking in support for asynchronous UI development, even though one of the main goals of these new keywords was support for responsive UI.

Interactive coding often introduces the need to start an operation, like an animation or a dialog box and then wait for an event before switching the state of the UI, for example: running another animation or removing a dialog. Reading this article you will learn how to do it better using the upcoming features of C# 5.0, regardless of whether you develop in WinRT, Silverlight, WPF, Windows Forms or even Web Forms. The source code that comes with this article contains a library you can use to cut the amount of code you need to write by half!

Continue reading

Tagged , , , , , ,

GridSplitters – Part 3: Limiting the Range of a GridSplitter

Sometimes I look at what people search for when they hit my blog and see some trending questions that don’t seem to have easily findable answers. This one seems to be related to how to set the limit for moving a GridSplitter… Well – you don’t actually touch the GridSplitter to do that. You need to modify your RowDefinition or ColumnDefinition. In a similar way that you can define MinWidth/MaxWidth/MinHeight/MaxHeight on any FrameworkElement – you can do MinWidth/MaxWidth on a ColumnDefinition and MinHeight/MaxHeight on a RowDefinition. By setting these – you limit how much the GridSplitter will be able to resize them. I actually take these into account in my SimpleGridSplitter implementation, so if you have lots of rows or columns in your WPF app – you can use that one. Silverlight works fine there with its own GridSplitter.

Continue reading

Tagged , , , , ,

“Moving GridSplitter with code behind” is not the right question

Occasionally I see people searching for how to move the GridSplitter with code behind…
This is not the right question. You can in theory move the GridSplitter itself with code – it is just a control that is hosted in a Grid (if used correctly), so by adjusting the Margins or ~Alignment properties – you can change its position, but what you really want is to resize the associated column/row – which will also incidentally move the GridSplitter.

Continue reading

Tagged , , , , ,

Fixing WPF GridSplitter

Introduction

While working on a WPF tool for work – I was creating ColumnDefinitions in a Grid dynamically (with code) and also dynamically adding GridSplitters to allow users (myself) to resize the columns. Well, it all worked OK, except for the many times that it did not. I thought I made some mistake when creating these GridSplitters, but it seemed like some other people had similar problems with GridSplitters, namely sometimes – they would not work when used with a mouse. When you drag a splitter – it would spring back to original position and stop being dragged or it would jump to some other random (?) position – maybe resizing one of the columns to 0. I tried to fix it by configuring the GridSplitters in different ways – in code behind, between Begin/EndInit blocks, in XAML – nothing helped. I simplified the repro and it seemed to be happening more often when the Grid had many GridSplitters. I checked the same layout in Silverlight and it worked without any problems (well, except for needing to reference another library since GridSplitters are not available in Silverlight using the basic Visual Studio application template). This seemed to indicate the WPF implementation is just somehow broken and it won’t work. Well then I guess I should implement the control myself – how hard would it be? – handle some mouse and keyboard events and resize an associated ColumnDefinition or RowDefinition…

The Fix – SimpleGridSplitter

Continue reading

Tagged , , , , ,

Windows RunTime – Timeline of Influences

(Note – while I work for Microsoft, my team was not involved in developing the technologies mentioned below and the thoughts and opinions on this blog are mine only)

This is what Microsoft showed at Build:

image

Microsoft at its spirit – really trying to support everything and everyone – the slide is actually oversimplified, but the Desktop Apps part seems to encompass and indicate support for all the legacy apps, languages and APIs, while Metro style (or Immersive) Apps support the same languages on top of a new set of APIs. Whichever language you used before – you can still use it to create desktop or web applications as well as the new Metro style apps. The APIs are different, but seem to be highly influenced by the .NET technologies.

Thinking about the above made me think about all the influences that brought the above to life. Here’s a timeline I came up with:

image

Complicated? You bet!

Timeline: the blocks should be placed fairly chronologically top to bottom.
Dates: were picked arbitrarily from Wikipedia based on dates of initial public appearance or if available – on release dates of the APIs/frameworks/host environments.
Colors:
L
ight blue (aqua) blocks represent languages, with the shaded ones being the languages available for use with WinRT.
Purple blocks represent Windows API and frameworks that wrap its UI components.
Dark blue blocks show DirectX, DX-based WPF and WPF derived Silverlight (desktop and mobile).
Arrows indicate successors (eg. Winforms –> WPF), dependencies (eg. DX –> WPF) or influences successors (eg. VCL –> WinForms).

Tagged , , , , , , , , ,

Using attached properties to style RowDefinitions of a Grid in WPF

The below sample shows how you can use attached dependency properties to style a Grid’s RowDefinitions. This specific case shows how a single property can be set to specify the number of rows in a Grid.

<Window
    x:Class="GridExtensions.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:GridExtensions="clr-namespace:GridExtensions"
    Title="MainWindow"
    Height="350"
    Width="525">
    <Grid>
        <Grid.Style>
            <Style>
                <Setter Property="GridExtensions:Griddier.RowsCount" Value="4"/>
            </Style>
        </Grid.Style>
        <TextBlock Grid.Row="0" Text="0"/>
        <TextBlock Grid.Row="1" Text="1"/>
        <TextBlock Grid.Row="2" Text="2"/>
        <TextBlock Grid.Row="3" Text="3"/>
    </Grid>
</Window>
namespace GridExtensions
{
    using System;
    using System.Windows;
    using System.Windows.Controls;

    public static class Griddier
    {
        #region RowsCount

        /// <summary>
        /// RowsCount Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty RowsCountProperty =
            DependencyProperty.RegisterAttached("RowsCount", typeof(int), typeof(Griddier),
                new FrameworkPropertyMetadata((int)0,
                    new PropertyChangedCallback(OnRowsCountChanged)));

        /// <summary>
        /// Gets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static int GetRowsCount(DependencyObject d)
        {
            return (int)d.GetValue(RowsCountProperty);
        }

        /// <summary>
        /// Sets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static void SetRowsCount(DependencyObject d, int value)
        {
            d.SetValue(RowsCountProperty, value);
        }

        /// <summary>
        /// Handles changes to the RowsCount property.
        /// </summary>
        private static void OnRowsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d == null)
                throw new ArgumentNullException("d");
            var grid = d as Grid;
            if (grid == null)
                throw new ArgumentException("d should be of type Grid.", "d");
            int newRowsCount = (int)d.GetValue(RowsCountProperty);

            if (grid.RowDefinitions.Count > newRowsCount)
                while (grid.RowDefinitions.Count > newRowsCount)
                    grid.RowDefinitions.RemoveAt(newRowsCount);

            var gl = (GridLength)(new GridLengthConverter().ConvertFromString("*"));

            for (int i = 0; i < newRowsCount; i++)
            {
                if (grid.RowDefinitions.Count > i)
                    grid.RowDefinitions[i].Height = gl;
                else
                    grid.RowDefinitions.Add(new RowDefinition {Height = gl});
            }
        }

        #endregion
    }
}
Tagged , , , , , ,

Binding to properties of a custom property in WPF, Silverlight 4 or Windows Phone 7

I was trying to create a UserControl for Windows Phone 7 yesterday and I wanted to enable specifying a  collection-type property that I could configure in my View/XAML file.

I have registered a DependencyProperty, but since I have not done many UserControls or CustomControls in WPF or SL, so I thought I would look how it is done in other similar places – like Grid.ColumnDefinitions. OK, so it is a custom type RowDefinitionCollection based on PresentationFrameworkCollection<RowDefinition>. I thought an ObservableCollection<MyItemType> or a List<> would work, but something did not work right, so I thought derive from PresentationFrameworkCollection<>, but when I tried – it turned out it does not have an accessible parameterless constructor which turned out to be some sort of an issue. I really wanted to make it work, so I thought – OK, I will implement my own version, so I created my collection type as a wrapper around an ObservableCollection, that derives from a FrameworkElement (I played with DependencyObject too) and implements all these interfaces – ICollection, ICollection<>, IList, IList<>, IEnumerable, IEnumerable<>.

All was good – I defined MyItemType as deriving from a FrameworkElement too and added some properties, among others an ICommand-typed-Command and CommandParameter. All hell broke loose when I tried to bind to the Command in XAML.

I kept getting these XamlParseException AG_E_PARSER_BAD_PROPERTY_VALUE on the line that had the binding to the Command. I tried looking at this from all angles of Google and Bing and could not find the right solution, until I tried the below and got the same problem!

<Grid>
    <Slider
        x:Name="slider"
        VerticalAlignment="Top"
        HorizontalAlignment="Stretch"
        Minimum="0"
        Maximum="100" />

    <Grid
        VerticalAlignment="Bottom"
        HorizontalAlignment="Stretch"
        Height="100">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{Binding Value, ElementName=slider}"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
 
        <Rectangle
            Fill="Red"
            Grid.Column="0"
            VerticalAlignment="Stretch"
            HorizontalAlignment="Stretch" />
    </Grid>
</Grid>

OK, so it seems like binding on ColumnDefinitions is not supported on WP7 either. Tried the same snippet in WPF – worked great. Silverlight 4 – no exception, but it just would not update my rectangle. So it seems like Silverlight is limited in that regard.

That was close – I started looking into it more and found that it would almost work, but the DataContext on my collection would not get updated whatever I do. Finally – I found two things I needed to do:

  • Handle DataContext changes on my UserControl and update the DataContext on my collection and do the same from the collection to MyItemType. Only thing – there is no DataContextChanged event. Sheesh… Fortunately we have our MVPs to the rescue – I found a solution on CodeProject quite easily – http://www.codeproject.com/Articles/38559/Silverlight-DataContext-Changed-Event.aspx – just added IDataContextChangedHandler to both the control and the collection and update the DataContext on their properties.
  • Made sure that MyItemsProperty DependencyProperty registration does not specify the default value as “new MyItemsCollection(MyUserControl)”, but rather do it in the constructor – otherwise all instances of the control would share the same collection! This is said to cause problems when applying styles to controls, since setting the value on the control overrides anything being set by the style, but I will not be bothered by it, since the control implementation isn’t lookless or re-template-able anyway.

It works!

Tagged , , ,

Fixing full-screen WPF windows

2011-12-13 – Note – if you got here trying to find out how to create a full-screen WPF window – you need to set WindowState=”Maximized” and WindowStyle=”None”. This post is about a workaround for an issue that will sometimes arise when doing this.

A while back when I was working on a full-screen WPF application – I found a bug when sometimes, especially after you have changed your screen resolution – the application would show up with a visible bright border, which looks pretty ugly when the UI is mostly black and the display has a black frame as most displays seem to these days. I have found two related bugs reported, but only vague description of a workaround and no fix.

  1. https://connect.microsoft.com/VisualStudio/feedback/details/363288/maximised-wpf-window-not-covering-full-screen?wa=wsignin1.0
  2. https://connect.microsoft.com/VisualStudio/feedback/details/540394/maximized-window-does-not-cover-working-area-after-screen-setup-change?wa=wsignin1.0

I have managed to get some hints for how to solve it to a reusable copy&paste fix I am now using in various places and I thought I would share it:

1. Hook up the handler for the SourceInitialized event in your maximized window’s XAML or code behind and define it as below:

public MainWindow()
{
    InitializeComponent();
    this.SourceInitialized += mainWindow_SourceInitialized;
}

private void mainWindow_SourceInitialized(object sender, EventArgs e)
{
    IntPtr handle = (new WindowInteropHelper(this)).Handle;

    // Use WindowProc as the callback method
    // to process all native window messages.
    HwndSource.FromHwnd(handle)
        .AddHook(NativeMethods.MaximizedSizeFixWindowProc);
}

2. Add the NativeMethods class to your project.

using System;
using System.Runtime.InteropServices;

/// <summary>
/// Native Windows API methods and interfaces.
/// </summary>
internal static class NativeMethods
{
    #region Constants
    // The WM_GETMINMAXINFO message is sent to a window when the size or
    // position of the window is about to change.
    // An application can use this message to override the window's
    // default maximized size and position, or its default minimum or
    // maximum tracking size.
    private const int WM_GETMINMAXINFO = 0x0024;

    // Constants used with MonitorFromWindow()
    // Returns NULL.
    private const int MONITOR_DEFAULTTONULL = 0;

    // Returns a handle to the primary display monitor.
    private const int MONITOR_DEFAULTTOPRIMARY = 1;

    // Returns a handle to the display monitor that is nearest to the window.
    private const int MONITOR_DEFAULTTONEAREST = 2;
    #endregion

    #region Structs
    /// <summary>
    /// Native Windows API-compatible POINT struct
    /// </summary>
    [Serializable, StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int X;
        public int Y;
    }
    /// <summary>
    /// The RECT structure defines the coordinates of the upper-left
    /// and lower-right corners of a rectangle.
    /// </summary>
    /// <see cref="http://msdn.microsoft.com/en-us/library/dd162897%28VS.85%29.aspx"/>
    /// <remarks>
    /// By convention, the right and bottom edges of the rectangle
    /// are normally considered exclusive.
    /// In other words, the pixel whose coordinates are ( right, bottom )
    /// lies immediately outside of the the rectangle.
    /// For example, when RECT is passed to the FillRect function, the rectangle
    /// is filled up to, but not including,
    /// the right column and bottom row of pixels. This structure is identical
    /// to the RECTL structure.
    /// </remarks>
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        /// <summary>
        /// The x-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public int Left;

        /// <summary>
        /// The y-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public int Top;

        /// <summary>
        /// The x-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public int Right;

        /// <summary>
        /// The y-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public int Bottom;
    }

    /// <summary>
    /// The MINMAXINFO structure contains information about a window's
    /// maximized size and position and its minimum and maximum tracking size.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms632605%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct MINMAXINFO
    {
        /// <summary>
        /// Reserved; do not use.
        /// </summary>
        public POINT Reserved;

        /// <summary>
        /// Specifies the maximized width (POINT.x)
        /// and the maximized height (POINT.y) of the window.
        /// For top-level windows, this value
        /// is based on the width of the primary monitor.
        /// </summary>
        public POINT MaxSize;

        /// <summary>
        /// Specifies the position of the left side
        /// of the maximized window (POINT.x)
        /// and the position of the top
        /// of the maximized window (POINT.y).
        /// For top-level windows, this value is based
        /// on the position of the primary monitor.
        /// </summary>
        public POINT MaxPosition;

        /// <summary>
        /// Specifies the minimum tracking width (POINT.x)
        /// and the minimum tracking height (POINT.y) of the window.
        /// This value can be obtained programmatically
        /// from the system metrics SM_CXMINTRACK and SM_CYMINTRACK.
        /// </summary>
        public POINT MinTrackSize;

        /// <summary>
        /// Specifies the maximum tracking width (POINT.x)
        /// and the maximum tracking height (POINT.y) of the window.
        /// This value is based on the size of the virtual screen
        /// and can be obtained programmatically
        /// from the system metrics SM_CXMAXTRACK and SM_CYMAXTRACK.
        /// </summary>
        public POINT MaxTrackSize;
    }

    /// <summary>
    /// The WINDOWINFO structure contains window information.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms632610%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct WINDOWINFO
    {
        /// <summary>
        /// The size of the structure, in bytes.
        /// The caller must set this to sizeof(WINDOWINFO).
        /// </summary>
        public uint Size;

        /// <summary>
        /// Pointer to a RECT structure
        /// that specifies the coordinates of the window.
        /// </summary>
        public RECT Window;

        /// <summary>
        /// Pointer to a RECT structure
        /// that specifies the coordinates of the client area.
        /// </summary>
        public RECT Client;

        /// <summary>
        /// The window styles. For a table of window styles,
        /// <see cref="http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx">
        /// CreateWindowEx
        /// </see>.
        /// </summary>
        public uint Style;

        /// <summary>
        /// The extended window styles. For a table of extended window styles,
        /// see CreateWindowEx.
        /// </summary>
        public uint ExStyle;

        /// <summary>
        /// The window status. If this member is WS_ACTIVECAPTION,
        /// the window is active. Otherwise, this member is zero.
        /// </summary>
        public uint WindowStatus;

        /// <summary>
        /// The width of the window border, in pixels.
        /// </summary>
        public uint WindowBordersWidth;

        /// <summary>
        /// The height of the window border, in pixels.
        /// </summary>
        public uint WindowBordersHeight;

        /// <summary>
        /// The window class atom (see
        /// <see cref="http://msdn.microsoft.com/en-us/library/ms633586%28VS.85%29.aspx">
        /// RegisterClass
        /// </see>).
        /// </summary>
        public ushort WindowType;

        /// <summary>
        /// The Windows version of the application that created the window.
        /// </summary>
        public ushort CreatorVersion;
    }

    /// <summary>
    /// The MONITORINFO structure contains information about a display monitor.
    /// The GetMonitorInfo function stores information in a MONITORINFO structure.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd145065%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct MONITORINFO
    {
        /// <summary>
        /// The size, in bytes, of the structure. Set this member
        /// to sizeof(MONITORINFO) (40) before calling the GetMonitorInfo function.
        /// Doing so lets the function determine
        /// the type of structure you are passing to it.
        /// </summary>
        public int Size;

        /// <summary>
        /// A RECT structure that specifies the display monitor rectangle,
        /// expressed in virtual-screen coordinates.
        /// Note that if the monitor is not the primary display monitor,
        /// some of the rectangle's coordinates may be negative values.
        /// </summary>
        public RECT Monitor;

        /// <summary>
        /// A RECT structure that specifies the work area rectangle
        /// of the display monitor that can be used by applications,
        /// expressed in virtual-screen coordinates.
        /// Windows uses this rectangle to maximize an application on the monitor.
        /// The rest of the area in rcMonitor contains system windows
        /// such as the task bar and side bars.
        /// Note that if the monitor is not the primary display monitor,
        /// some of the rectangle's coordinates may be negative values.
        /// </summary>
        public RECT WorkArea;

        /// <summary>
        /// The attributes of the display monitor.
        ///
        /// This member can be the following value:
        /// 1 : MONITORINFOF_PRIMARY
        /// </summary>
        public uint Flags;
    }
    #endregion

    #region Imported methods
    /// <summary>
    /// The GetWindowInfo function retrieves information about the specified window.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms633516%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="pwi">The reference to WINDOWINFO structure.</param>
    /// <returns>true on success</returns>
    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);

    /// <summary>
    /// The MonitorFromWindow function retrieves a handle to the display monitor
    /// that has the largest area of intersection with the bounding rectangle
    /// of a specified window.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd145064%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="dwFlags">Determines the function's return value
    /// if the window does not intersect any display monitor.</param>
    /// <returns>
    /// Monitor HMONITOR handle on success or based on dwFlags for failure
    /// </returns>
    [DllImport("user32.dll")]
    private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);

    /// <summary>
    /// The GetMonitorInfo function retrieves information about a display monitor
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd144901%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hMonitor">A handle to the display monitor of interest.</param>
    /// <param name="lpmi">
    /// A pointer to a MONITORINFO structure that receives information
    /// about the specified display monitor.
    /// </param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
    #endregion

    /// <summary>
    /// Window procedure callback.
    /// Hooked to a WPF maximized window works around a WPF bug:
    /// https://connect.microsoft.com/VisualStudio/feedback/details/363288/maximised-wpf-window-not-covering-full-screen?wa=wsignin1.0#tabs
    /// possibly also:
    /// https://connect.microsoft.com/VisualStudio/feedback/details/540394/maximized-window-does-not-cover-working-area-after-screen-setup-change?wa=wsignin1.0
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="msg">The window message.</param>
    /// <param name="wParam">The wParam (word parameter).</param>
    /// <param name="lParam">The lParam (long parameter).</param>
    /// <param name="handled">
    /// if set to <c>true</c> - the message is handled
    /// and should not be processed by other callbacks.
    /// </param>
    /// <returns></returns>
    internal static IntPtr MaximizedSizeFixWindowProc(
        IntPtr hwnd,
        int msg,
        IntPtr wParam,
        IntPtr lParam,
        ref bool handled)
    {
        switch (msg)
        {
            case WM_GETMINMAXINFO:
                // Handle the message and mark it as handled,
                // so other callbacks do not touch it
                WmGetMinMaxInfo(hwnd, lParam);
                handled = true;
                break;
        }
        return (IntPtr)0;
    }

    /// <summary>
    /// Creates and populates the MINMAXINFO structure for a maximized window.
    /// Puts the structure into memory address given by lParam.
    /// Only used to process a WM_GETMINMAXINFO message.
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="lParam">The lParam.</param>
    private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    {
        // Get the MINMAXINFO structure from memory location given by lParam
        MINMAXINFO mmi =
            (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Get the monitor that overlaps the window or the nearest
        IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        if (monitor != IntPtr.Zero)
        {
            // Get monitor information
            MONITORINFO monitorInfo = new MONITORINFO();
            monitorInfo.Size = Marshal.SizeOf(typeof(MONITORINFO));
            GetMonitorInfo(monitor, ref monitorInfo);

            // The display monitor rectangle.
            // If the monitor is not the primary display monitor,
            // some of the rectangle's coordinates may be negative values
            RECT rcMonitorArea = monitorInfo.Monitor;

            // Get window information
            WINDOWINFO windowInfo = new WINDOWINFO();
            windowInfo.Size = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
            GetWindowInfo(hwnd, ref windowInfo);
            int borderWidth = (int)windowInfo.WindowBordersWidth;
            int borderHeight = (int)windowInfo.WindowBordersHeight;

            // Set the dimensions of the window in maximized state
            mmi.MaxPosition.X = -borderWidth;
            mmi.MaxPosition.Y = -borderHeight;
            mmi.MaxSize.X =
                rcMonitorArea.Right - rcMonitorArea.Left + 2 * borderWidth;
            mmi.MaxSize.Y =
                rcMonitorArea.Bottom - rcMonitorArea.Top + 2 * borderHeight;

            // Set minimum and maximum size
            // to the size of the window in maximized state
            mmi.MinTrackSize.X = mmi.MaxSize.X;
            mmi.MinTrackSize.Y = mmi.MaxSize.Y;
            mmi.MaxTrackSize.X = mmi.MaxSize.X;
            mmi.MaxTrackSize.Y = mmi.MaxSize.Y;
        }

        // Copy the structure to memory location specified by lParam.
        // This concludes processing of WM_GETMINMAXINFO.
        Marshal.StructureToPtr(mmi, lParam, true);
    }
}

(Source code at dropbox.)

Tagged , , ,