In my previous post: Creating a Custom Window Style, I showed how to create a custom Style in WPF using WindowStyle.None and ResizeMode.NoResize. What bugged me about this approach was that you can't move the custom window, because you have no Window chrome to drag around the screen.
I read one article that took a really “hard core” approach and used interop to use the SendMessage
, ReleaseCapture
, and MouseMove
methods, but this is a very Win32-oriented approach and in actual fact, WPF provides a much easier approach.
To enable a user to move your window from any area on that window (not necessarily just the title if you want to do something really different), you simply handle the MouseLeftButtonDown event for a UIElement on your window and call the DragMove method, as shown in the following code example:
private void title_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
Hooking it Up to the Style
Having defined the look-and-feel in a reusable Style, we need a way of hooking up the MouseLeftButtonDown event and handling it in our Window class. Unfortunately, this is a little bit tricky. If the Style is defined in the XAML that defines your Window, then you can simply add the event handler for the relevant UIElement as you would for any other element. However, putting your Style in with your actual Window doesn't give you a very reusable Style. Instead, give a fixed name to the element in your Style, such as PART_Title
, and then get a reference to that element and hook the event in your Window class. Are you still with me?
The following XAML code example shows the named part in the Window Style.
<style targettype=""{x:Type" type="text/css" window="" x:key=""MessageBox"">...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{DynamicResource MessageBoxBackgroundBrush}"
BorderBrush="{DynamicResource MessageBoxBorderBrush}"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid x:Name="PART_Title">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF214F18" Offset="0"/>
<GradientStop Color="#FF20361C" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<Label Style="{DynamicResource MessageBoxTitle}"
Content="{TemplateBinding Title}" />
<Button x:Name="PART_Close"
Content="{DynamicResource CloseButtonPath}"
Grid.Column="1"
Style="{DynamicResource CloseButton}"
Padding="4" />
</Grid>
<AdornerDecorator Grid.Row="1">
<ContentPresenter Content="{TemplateBinding Content}"
Margin="{TemplateBinding Margin}" />
</AdornerDecorator>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter></style>
All I've done from the previous version of the Style is to add an x:Name
attribute with a value of PART_Title
to the Grid that contains the title of the Window.
The following code example shows how to get a reference to that element and hook the mouse event in the CustomMessageBox
class.
namespace CustomWindow
{
public partial class CustomMessageBox : Window
{
private MessageBoxResult _result = MessageBoxResult.None;
private Button _close;
private FrameworkElement _title;
...
private void this_Loaded(object sender, RoutedEventArgs e)
{
this._close = (Button)this.Template.FindName("PART_Close", this);
if (null != this._close)
{
if (false == this._cancel.IsVisible)
{
this._close.IsCancel = false;
}
}
this._title = (FrameworkElement)this.Template.FindName("PART_Title", this);
if (null != this._title)
{
this._title.MouseLeftButtonDown +=
new MouseButtonEventHandler(title_MouseLeftButtonDown);
}
}
private void title_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
...
}
}
Summary
So, if you want to enable window movement for custom styled windows, then you need to call the DragMove method from an event handler for the MouseLeftButtonDown event. In this example, the CustomMessageBox
class provides that event handling. If you are using a Style that applies to multiple windows, then you will need to hook the mouse event in each class, or implement a base class that provides this functionality for you and inherit from that class.
As before, I've provided the (now updated) source code for the custom message box class.