MULTIMEDIA

Building Out Of Browser Silverlight Applications - Controlling the Application Window

1/19/2011 11:43:15 AM

1. Problem

You want to control various aspects of the application window such as its position, size, chrome, move, and resize behavior.

2. Solution

Use the properties and methods of the Window class, as well as the WindowState and WindowStyle settings.

3. How It Works

3.1. Window Attributes

When an application is running out of browser, the Application.Current.MainWindow property, which is an instance of the Window class, represents the running window. The Window class exposes several attributes of the running application window that can be programmatically controlled. Window.Top, Window.Left, Window.Height and Window.Width expose the top and left coordinates and the height and width of the window, respectively.

Additionally the Window.Topmost property, when set to true, makes the application window the topmost window in Z-order on the current desktop, and the Window.WindowState property can be set to one of the values in the enumerated type WindowState that include WindowState.Maximized, WindowState.MinimizedWindowState.Normal to define the various possible states of the window, with WindowState.Normal being the default setting. and

The application can also define a specific window style as defined in the WindowStyle enumerated type. This attribute can however only be read at runtime through the Deployment.Current.OutOfBrowserSettings.WindowSettings.WindowStyle property, and requires that it be set through the out of browser settings at deployment. The WindowStyle enumeration defines three WindowStyle values: None, SingleBorderWindow, and BorderlessRoundCornersWindow. WindowStyle.None and WindowStyle.BorderlessRoundCornersWindow both produce borderless windows, with the second option adding rounded corners. WindowStyle.SingleBorderWindow uses the default OS specific chrome. Figure 1 shows the Out-of-Browser settings dialog with the window style setting being set.

Figure 1. Window Style setting for OOB deployment

Note that the Deployment.Current.OutOfBrowserSettings.WindowSettings property provides access to other settings information such as the window title, the initial left-top coordinates, initial dimensions etc., but all of these properties are read only. Some of the properties that can be changed at runtime will need to be changed through the Window class, as discussed earlier. Also note that specifying the window style requires that the application be marked to run with elevated trust when run out of the browser. You should be aware that when an OOB application is installed with elevated trust requirements, the install dialog looks a little different. Figure 2 shows the install dialog for an OOB application requiring elevated trust.

Figure 2. Install dialog for application requiring elevated trust

.3.2. Resizing and moving a Window

In addition to the above window attributes, there are a few additional APIs provided to control the resizing and moving of a window. The default OS-supplied window chrome provides you with the necessary means to resize or move the window. But in cases where you set the WindowStyle property to one of the values that create a borderless window, you may need to offer alternative means to the user to resize and move the window. The Window.DragMove() method can be used to move the window programmatically, especially in response to mouse events. DragMove() automatically moves the window in the direction in which the mouse moves, by the amount that the mouse moves by between calls to DragMove(). The Window.DragResize() method can be called to resize the window. DragResize() accepts a single parameter of the enumerated type WindowResizeEdge. The WindowResizeEdge defines the possible window edge values, one for each edge and one for each corner, such as WindowResizeEdge.Bottom, WindowResizeEdge.BottomLeft, etc. DragResize () automatically resizes the window by the amount the mouse has moved between calls to DragResize ().Note that both APIs require the application to be installed and running with elevated trust.

8.2.4. The Code

The sample for this recipe extends the note taker application from this article to demonstrate the usage of some of the attributes and APIs discussed above.

You modify the XAML and add three buttons to the top right corner of the LayoutRoot grid: a Button to minimize the window named btnMinimize, a Button to toggle between the maximized and the normal view of the window named btnMaximize, and a Button to close the window named btnClose. You then change the out of browser settings for the application and set the Window Style field to "No Border" to deploy it as a borderless window. Since the changes to the XAML are minimal, we do not list them here; you can refer to the MainPage.xaml for the code sample to take a look at the changes. Figure 8-10 shows the application running in a borderless window with the three buttons mentioned above.

Figure 3. Offline Note taker application running in a borderless window style

You centralize the handling of the window resizing and moving functionality as well as the click handlers to the three buttons mentioned above in a single class named WindowManager. Listing 1 shows the code for the WindowManager class.

Listing 1. WindowManager class
public static class WindowManager
{
private static FrameworkElement ShellRoot;
//a rect defined on the window determining the hit target that
//we will use to determine if a mouse drag causes the window to move
private static Rect MoveHandleRect = default(Rect);
//the width from the edges of the window determining the hit target
//we will use to determine if a mouse drag causes a resize
private static double ResizeHandleWidth = 8;
//store the old cursor to revert back when necessary
static Cursor OldCursor = null;
//current action on the window
static Action CurrentAction = Action.None;

//enumeration defining the current action on the window
private enum Action
{


Moving, Resizing, None
}
//register the FrameworkElement whose mouse movements
//will determine the various window move and resize logic
public static void RegisterShell(FrameworkElement shellRoot,
double resizeHandleWidth, Rect moveHandleRect,
Button btnMinimize, Button btnMaximize, Button btnClose)
{
ShellRoot = shellRoot;
ResizeHandleWidth = resizeHandleWidth;
MoveHandleRect = moveHandleRect;
OldCursor = ShellRoot.Cursor;
//handle the various mouse events
ShellRoot.MouseEnter += new MouseEventHandler(ShellRoot_MouseEnter);
ShellRoot.MouseLeave += new MouseEventHandler(ShellRoot_MouseLeave);
ShellRoot.MouseMove += new MouseEventHandler(ShellRoot_MouseMove);
ShellRoot.MouseLeftButtonDown +=
new MouseButtonEventHandler(ShellRoot_MouseLeftButtonDown);
ShellRoot.MouseLeftButtonUp +=
new MouseButtonEventHandler(ShellRoot_MouseLeftButtonUp);
//handle the control button events
btnClose.Click += new RoutedEventHandler(btnClose_Click);
btnMaximize.Click += new RoutedEventHandler(btnMaximize_Click);
btnMinimize.Click += new RoutedEventHandler(btnMinimize_Click);
}

private static void btnMinimize_Click(object sender,
System.Windows.RoutedEventArgs e)
{
Application.Current.MainWindow.WindowState = WindowState.Minimized;
}

private static void btnMaximize_Click(object sender,
System.Windows.RoutedEventArgs e)
{
Application.Current.MainWindow.WindowState =
Application.Current.MainWindow.WindowState == WindowState.Maximized ?
WindowState.Normal : WindowState.Maximized;
}

private static void btnClose_Click(object sender,
System.Windows.RoutedEventArgs e)
{
Application.Current.MainWindow.Close();
}


private static void ShellRoot_MouseEnter(object sender, MouseEventArgs e)
{
SetMouseCursor(e);
}
static void ShellRoot_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e)
{
CurrentAction = (GetCurrentResizeEdge(e) != default(WindowResizeEdge)) ?
Action.Resizing : (IsMouseOnMoveZone(e) ? Action.Moving : Action.None);

}
static void ShellRoot_MouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
CurrentAction = Action.None;
}

static void ShellRoot_MouseMove(object sender, MouseEventArgs e)
{
if (CurrentAction == Action.Resizing)
{
Application.Current.MainWindow.DragResize(GetCurrentResizeEdge(e));
}
else if (CurrentAction == Action.Moving)
{
Application.Current.MainWindow.DragMove();
}
else
SetMouseCursor(e);
}

static void ShellRoot_MouseLeave(object sender, MouseEventArgs e)
{
if (CurrentAction != Action.None)
{
CurrentAction = Action.None;
SetMouseCursor(e);
}
}

private static void SetMouseCursor(MouseEventArgs e)
{
WindowResizeEdge ResizeZone = GetCurrentResizeEdge(e);

if (ResizeZone != default(WindowResizeEdge) && OldCursor == default(Cursor))
OldCursor = ShellRoot.Cursor;

switch (ResizeZone)
{
case WindowResizeEdge.Top:
case WindowResizeEdge.Bottom:
ShellRoot.Cursor = Cursors.SizeNS;
break;
case WindowResizeEdge.Left:
case WindowResizeEdge.Right:
ShellRoot.Cursor = Cursors.SizeWE;
break;
case WindowResizeEdge.TopLeft:
case WindowResizeEdge.BottomRight:
ShellRoot.Cursor = Cursors.SizeNWSE;
break;
case WindowResizeEdge.TopRight:
case WindowResizeEdge.BottomLeft:
ShellRoot.Cursor = Cursors.SizeNESW;
break;
default:
ShellRoot.Cursor = OldCursor;
OldCursor = default(Cursor);
break;
}
}

private static bool IsMouseOnMoveZone(MouseEventArgs e)
{
return Application.Current.MainWindow.WindowState ==
WindowState.Maximized ? false :
MoveHandleRect.Contains(e.GetPosition(ShellRoot));
}

private static WindowResizeEdge GetCurrentResizeEdge(MouseEventArgs e)
{
WindowResizeEdge RetVal = default(WindowResizeEdge);
if (Application.Current.MainWindow.WindowState == WindowState.Maximized)
return RetVal;

Point CurPos = e.GetPosition(ShellRoot);
if (CurPos.X < ResizeHandleWidth)
{
if (CurPos.Y < ResizeHandleWidth)

RetVal = WindowResizeEdge.TopLeft;
else if (CurPos.Y > ShellRoot.ActualHeight - ResizeHandleWidth)
RetVal = WindowResizeEdge.BottomLeft;
else
RetVal = WindowResizeEdge.Left;
}
else if (CurPos.X > ShellRoot.ActualWidth - ResizeHandleWidth)
{
if (CurPos.Y < ResizeHandleWidth)
RetVal = WindowResizeEdge.TopRight;
else if (CurPos.Y > ShellRoot.ActualHeight - ResizeHandleWidth)
RetVal = WindowResizeEdge.BottomRight;
else
RetVal = WindowResizeEdge.Right;
}
else
{
if (CurPos.Y < ResizeHandleWidth)
RetVal = WindowResizeEdge.Top;
else if (CurPos.Y > ShellRoot.ActualHeight - ResizeHandleWidth)
RetVal = WindowResizeEdge.Bottom;
else
RetVal = default(WindowResizeEdge);
}
return RetVal;
}
}


The WindowManager class exposes a static method named RegisterShell() which takes in all the parameters required for the WindowManager to enable window resize and moving. The shellRoot parameter is the FrameworkElement instance whose mouse events the WindowManager attaches to implement the resize and move logic. This would typically be a high level container in your visual tree that covers the entire window area, such as the top level grid in your window design. The resizeHandleWidth parameter determines the number of pixels from each edge of the window within which a mouse drag is considered a cause for resize. The moveHandleRect defines the dimensions of a rectangular area measured in terms of the coordinates of the shellRoot element, within which a mouse drag is considered a cause for a window move. Lastly, the three button parameters refer to the three control buttons on the window. The code for RegisterShell simply attaches handlers to the appropriate events on the shellRoot and the buttons.

In the shellRoot_MouseEnter () handler, you set the mouse cursor appropriately by calling SetMouseCursor() and in shellRoot_MouseLeave(), you reset the CurrentAction variable to Action.None to indicate that neither a resize nor a move is happening any more, and call SetMouseCursor() again.

SetMouseCursor() accepts a MouseEventArgs parameter passed in from the mouse event handlers, and first calls GetCurrentResizeEdge() to get the resize edge the mouse might be on. If a valid WindowResizeEdge is returned from GetCurrentResizeEdge(), you proceed to set the mouse cursor to the appropriate value depending on the resize edge the mouse is currently on.

GetCurrentResizeEdge() also accepts a MouseEventArgs and works out the WindowResizeEdge by comparing the current mouse position to the calculated resize edges based on the ResizeHandleWidth value and the ShellRoot dimensions.

On ShellRoot_MouseLeftButtonDown() you set the CurrentAction to Action.Resizing if GetCurrentResizeEdge() reports the mouse to be on a valid edge, or else to CurrentAction.Moving if IsMouseOnMoveZone() reports true. IsMouseOnMoveZone() also accepts the MouseEventArgs and uses the current mouse coordinates to check if the mouse position falls within the rectangle designated by MoveHandleRect. On ShellRoot_MouseLeftButtonUp() you reset the CurrentAction variable. And finally on ShellRoot_MouseMove() handler, you either call Window.DragMove() or Window.DragResize() depending on the value of the CurrentAction variable.

To handle processing the control button clicks, in btnMinimize_Click() you set the WindowState of the MainWindow to WindowState.Minimized. In btnMaximizedClick() you set the WindowState to Normal if the window is already maximized or you maximize it if it is not. And in btnClose_Click() you close the MainWindow, causing the application to exit. Figure 4 shows the application window being resized by dragging the right edge.

Figure 4. Borderless window being resized

Other  
  •  iPhone 3D Programming : Adding Depth and Realism - Loading Geometry from OBJ Files
  •  iPhone 3D Programming : Adding Depth and Realism - Better Wireframes Using Polygon Offset
  •  Programming with DirectX : Textures in Direct3D 10 (part 2)
  •  Programming with DirectX : Textures in Direct3D 10 (part 1) - Textures Coordinates
  •  Programming with DirectX : Shading and Surfaces - Types of Textures
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 2)
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 1) - New Rendering Engine
  •  iPhone 3D Programming : Adding Depth and Realism - Shaders Demystified
  •  Programming with DirectX : Transformation Demo
  •  Programming with DirectX : View Transformations
  •  Programming with DirectX : World Transformations
  •  Programming with DirectX : Projection Transformations
  •  iPhone 3D Programming : Adding Depth and Realism - Lighting Up (part 2)
  •  iPhone 3D Programming : Adding Depth and Realism - Lighting Up (part 1)
  •  iPhone 3D Programming : Adding Depth and Realism - Surface Normals (part 2)
  •  iPhone 3D Programming : Adding Depth and Realism - Surface Normals (part 1)
  •  iPhone 3D Programming : Adding Depth and Realism - Filling the Wireframe with Triangles
  •  iPhone 3D Programming : Adding Depth and Realism - Creating and Using the Depth Buffer
  •  iPhone 3D Programming : Adding Depth and Realism - Examining the Depth Buffer
  •  iPhone 3D Programming : HelloCone with Fixed Function
  •  
    Top 10
    Windows Vista : Installing and Running Applications - Launching Applications
    Windows Vista : Installing and Running Applications - Applications and the Registry, Understanding Application Compatibility
    Windows Vista : Installing and Running Applications - Practicing Safe Setups
    Windows Server 2003 : Domain Name System - Command-Line Utilities
    Microsoft .NET : Design Principles and Patterns - From Principles to Patterns (part 2)
    Microsoft .NET : Design Principles and Patterns - From Principles to Patterns (part 1)
    Brother MFC-J4510DW - An Innovative All-In-One A3 Printer
    Computer Planet I7 Extreme Gaming PC
    All We Need To Know About Green Computing (Part 4)
    All We Need To Know About Green Computing (Part 3)
    Most View
    Ipad Lion (Part 2) - What to expect from the upcoming mountain lion
    Visual Studio Team System 2008 : Command Line (part 2)
    Straighten Teeth for Better Portraits (part 1)
    Samsung NX210 - Good Camera, A Little Pricey
    Windows Tips & Tricks (June 2012) – Part 4 - Facebook - Deactivate facial recognition, Post selectively
    .NET security : Programming Isolated Storage
    iPad Mini vs. Google Nexus 7
    Expert Computing Advice – January 2013 (Part 2)
    The Anti - iPad Assembly (Part 1) - Samsung Galaxy Tab 2 10.1, Acer Iconia Tab A700
    Need To Know A Snapshot Of What Is Exciting Us (Part 1) - New iPad enhances tablet photography
    10 Reasons Why Tripods Are Necessary For Cameras
    Windows Server 2008 Server Core : Accessing the Windows Package Manager with the PkgMgr Utility, Adding and Removing Applications with the OCSetup Utility
    Expert computing advice (Part 4)
    Windows 7 : General Maintenance Tools (part 3) - Checking Your Disks for Errors & Optimizing Disk Performance
    Display Warriors (part 3) - Quantum QHM1560, BenQ GL2030 & LG E2290
    SharePoint 2010 :Implementing a Partner Extranet Solution (part 1) - Creating the Extranet Web Application & Creating an Extranet Site Collection
    Samsung Galaxy Tab 2 10.1 - Falls From Premium To Midrange Tablet
    Home PC Group Test - December 2012 (Part 1) : Arbico i5357 eXcel, CCL Elite Raven, Chillblast Fusion Orb
    Canon Pixma E600 Printer Review
    Samsung Series 9 Notebook – One Step Over And Beyond