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