Making a Custom Window Resizable in WPF
This is a continuation of my Creating a Custom Window in WPF post. If we create a custom window, we want the window to behave like a normal window. One of those things are making the window resizable.
To make the window resizable, we need something to grab on to. We can’t use the border, because we can’t specify a side or corner of the border, so we will need to create our own border that we can grab.
Let’s start by making the top/right/bottom/left sides. Since our corner has a radius of 10, we’ll set margins on the sides so they don’t overlap the corner. We also want to put a negative margin so that the sides will overlap the border. There are some events here that we’ll use later.
<aero:SystemDropShadowChrome CornerRadius="10" Margin="10"> <Border BorderThickness="1" BorderBrush="Black" Background="White" Margin="0" CornerRadius="10"> <Grid> <Border Height="40" Background="#01000000" VerticalAlignment="Top" CornerRadius="10,10,0,0" MouseLeftButtonDown="DragWindow" /> <Rectangle x:Name="ResizeN" Fill="Yellow" VerticalAlignment="Top" Height="4" Margin="9,-2,9,0" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize" /> <Rectangle x:Name="ResizeE" Fill="Yellow" HorizontalAlignment="Right" Width="4" Margin="0,9,-2,9" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize" /> <Rectangle x:Name="ResizeS" Fill="Yellow" VerticalAlignment="Bottom" Height="4" Margin="9,0,9,-2" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize" /> <Rectangle x:Name="ResizeW" Fill="Yellow" HorizontalAlignment="Left" Width="4" Margin="-2,9,0,9" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize" /> </Grid> </Border> </aero:SystemDropShadowChrome>
Now we can create the corners with a radius of 10.
<Path x:Name="ResizeNW" VerticalAlignment="Top" HorizontalAlignment="Left" Stroke="Green" StrokeThickness="4" Margin="0" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure StartPoint="0,10"> <PathFigure.Segments> <PathSegmentCollection> <QuadraticBezierSegment Point1="0,0" Point2="10,0" /> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> <Path x:Name="ResizeNE" VerticalAlignment="Top" HorizontalAlignment="Right" Stroke="Green" StrokeThickness="4" Margin="0,0,-2,0" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure StartPoint="0,0"> <PathFigure.Segments> <PathSegmentCollection> <QuadraticBezierSegment Point1="10,0" Point2="10,10" /> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> <Path x:Name="ResizeSE" VerticalAlignment="Bottom" HorizontalAlignment="Right" Stroke="Green" StrokeThickness="4" Margin="0,0,-2,-2" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure StartPoint="10,0"> <PathFigure.Segments> <PathSegmentCollection> <QuadraticBezierSegment Point1="10,10" Point2="0,10" /> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> <Path x:Name="ResizeSW" VerticalAlignment="Bottom" HorizontalAlignment="Left" Stroke="Green" StrokeThickness="4" Margin="0,0,0,-2" MouseEnter="DisplayResizeCursor" MouseLeave="ResetCursor" PreviewMouseLeftButtonDown="Resize"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure StartPoint="0,0"> <PathFigure.Segments> <PathSegmentCollection> <QuadraticBezierSegment Point1="0,10" Point2="10,10" /> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path>
Now we need to make the corners and edges resizable. We also need to make the cursor change when it hovers the corners and edges. To do this, we implement the events I mentioned earlier.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Interop; using System.Runtime.InteropServices; namespace ResizableWindow { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { private const int WM_SYSCOMMAND = 0x112; private HwndSource hwndSource; private enum ResizeDirection { Left = 61441, Right = 61442, Top = 61443, TopLeft = 61444, TopRight = 61445, Bottom = 61446, BottomLeft = 61447, BottomRight = 61448, } [DllImport( "user32.dll", CharSet = CharSet.Auto )] private static extern IntPtr SendMessage( IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam ); public Window1() { SourceInitialized += Window1_SourceInitialized; InitializeComponent(); } private void Window1_SourceInitialized( object sender, EventArgs e ) { hwndSource = PresentationSource.FromVisual( (Visual)sender ) as HwndSource; } private void ResizeWindow( ResizeDirection direction ) { SendMessage( hwndSource.Handle, WM_SYSCOMMAND, (IntPtr)direction, IntPtr.Zero ); } protected void ResetCursor( object sender, MouseEventArgs e ) { if( Mouse.LeftButton != MouseButtonState.Pressed ) { this.Cursor = Cursors.Arrow; } } protected void Resize( object sender, MouseButtonEventArgs e ) { var clickedShape = sender as Shape; switch( clickedShape.Name ) { case "ResizeN": this.Cursor = Cursors.SizeNS; ResizeWindow( ResizeDirection.Top ); break; case "ResizeE": this.Cursor = Cursors.SizeWE; ResizeWindow( ResizeDirection.Right ); break; case "ResizeS": this.Cursor = Cursors.SizeNS; ResizeWindow( ResizeDirection.Bottom ); break; case "ResizeW": this.Cursor = Cursors.SizeWE; ResizeWindow( ResizeDirection.Left ); break; case "ResizeNW": this.Cursor = Cursors.SizeNWSE; ResizeWindow( ResizeDirection.TopLeft ); break; case "ResizeNE": this.Cursor = Cursors.SizeNESW; ResizeWindow( ResizeDirection.TopRight ); break; case "ResizeSE": this.Cursor = Cursors.SizeNWSE; ResizeWindow( ResizeDirection.BottomRight ); break; case "ResizeSW": this.Cursor = Cursors.SizeNESW; ResizeWindow( ResizeDirection.BottomLeft ); break; default: break; } } protected void DisplayResizeCursor( object sender, MouseEventArgs e ) { var clickedShape = sender as Shape; switch( clickedShape.Name ) { case "ResizeN": case "ResizeS": this.Cursor = Cursors.SizeNS; break; case "ResizeE": case "ResizeW": this.Cursor = Cursors.SizeWE; break; case "ResizeNW": case "ResizeSE": this.Cursor = Cursors.SizeNWSE; break; case "ResizeNE": case "ResizeSW": this.Cursor = Cursors.SizeNESW; break; default: break; } } protected void DragWindow( object sender, MouseButtonEventArgs e ) { DragMove(); } } }
Now the edges are resizable. We don’t want to keep the green and yellow coloring, so we can change all the colors to almost transparent by setting the colors to #01000000.
And now our window looks like normal again and is resizable.
You can download the example project here. Note: Be sure to change the extension from .doc to .zip.




