WPF Quick Tip: How to get WPF Window client area size

September 20, 2009

This time my quick tip will be not about the Silverlight, but about WPF.

When you have WPF window, the runtime size of it could be discovered by ActualWidth/ActualHeight properties. In most cases this is enough. But what about the case, when you need to perform some mathematical calculations based on the real size of client area? What is this client area?

Well, the second question is really easy one – the client area is the area available to user defined UI (controls, panes, etc.):

image

At the screenshot – client area is a pink part of the window.

The client area size could be calculated differently. First, most WPF-oriented method is to get ActuialWidth/ActualHeight from auto-sized top-level panel (in my case from Canvas):

<StackPanel Orientation="Horizontal">
    <TextBlock Text="Canvas Width:"/>
    <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Canvas, AncestorLevel=1}, Path=ActualWidth}" />
</StackPanel>
 
<StackPanel Orientation="Horizontal">
    <TextBlock Text="Canvas Height:"/>
    <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Canvas, AncestorLevel=1}, Path=ActualHeight}" />
</StackPanel>

This is very easy way, but what to do with a containers/panels which will auto-size according to child elements and not the Window? For this case there are two additional approaches.

Second approach is to use Win32 API to calculate the available size:

#region Native Methods
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
 
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
  public int Left;
  public int Top;
  public int Right;
  public int Bottom;
 
  public RECT(int left_, int top_, int right_, int bottom_)
  {
    Left = left_;
    Top = top_;
    Right = right_;
    Bottom = bottom_;
  }
 
  public int Height { get { return Bottom - Top; } }
  public int Width { get { return Right - Left; } }
  public Size Size { get { return new Size(Width, Height); } }
 
  public Point Location { get { return new Point(Left, Top); } }
 
  // Handy method for converting to a System.Drawing.Rectangle
  public Rect ToRectangle()
  { return new Rect(Left, Top, Right, Bottom); }
 
  public static RECT FromRectangle(Rect rectangle)
  {
    return new Rect(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
  }
 
  public override int GetHashCode()
  {
    return Left ^ ((Top << 13) | (Top >> 0x13))
      ^ ((Width << 0x1a) | (Width >> 6))
      ^ ((Height << 7) | (Height >> 0x19));
  }
 
  #region Operator overloads
 
  public static implicit operator Rect(RECT rect)
  {
    return rect.ToRectangle();
  }
 
  public static implicit operator RECT(Rect rect)
  {
    return FromRectangle(rect);
  }
 
  #endregion
}
 
public static RECT GetClientRect(IntPtr hWnd)
{
  RECT result = new RECT();
  GetClientRect(hWnd, out result);
  return result;
}
#endregion

Getting the size:

RECT rect;
GetClientRect(new WindowInteropHelper(this).Handle, out rect);
 
theWidth = rect.Width;
theHeight = rect.Height;

In this case we will get exactly the same values, like from ActualWidth/ActualHeight of auto-sized controls, but will work always.

 

The third approach is to calculated the size from Window size minus borders width and window caption height. This will give us estimation based on current theme, but in this case we need to know additional information (like WindowStyle – in my case I’m using “ThreeDBorderWindow”):

theWidth = App.Current.MainWindow.ActualWidth - (SystemParameters.ResizeFrameVerticalBorderWidth * 2);
theHeight = App.Current.MainWindow.ActualHeight - ((SystemParameters.ResizeFrameHorizontalBorderHeight * 2) + SystemParameters.WindowCaptionHeight);

The value is estimation, and should be used wisely.

 

Stay tuned for more…

 

Enjoy,

Alex

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

one comment

  1. Jürgen BayerApril 21, 2010 ב 11:02

    Great tip. I used the API approach and found out that you forgot to account for the current DPI setting that is easy to change in Windows. If the screen is set to 120 DPI for example the client rectangle is calculated to large (since WPF always works with 96 Points per inch).

    I’m using the following code:
    IntPtr windowHandle = new System.Windows.Interop.WindowInteropHelper(this).Handle;
    RECT rect;
    GetClientRect(windowHandle, out rect);
    var presentationSource = PresentationSource.FromVisual(this);
    double pixelCalculationFactorX = presentationSource.CompositionTarget.TransformFromDevice.M11;
    double pixelCalculationFactorY = presentationSource.CompositionTarget.TransformFromDevice.M22;
    double clientAreaWidth = (rect.Right – rect.Left) * pixelCalculationFactorX;
    double clientAreaHeight = (rect.Bottom – rect.Top) * pixelCalculationFactorY;
    Rect clientAreaRectangle = new Rect(0, 0, clientAreaWidth, clientAreaHeight);

    Reply