We started to build a generic MessageBox in the previous article and we started by creating a simple MessageBox content control (the surface for our dialogs). This time, we will be putting things together to actually show the dialog as a popup.
The basic idea for the MessageBox.Show is to create a System.Windows.Controls.Primitives.Popup and place the dialog surface inside of it.
public static class MessageBox {
public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
Popup popup = new Popup();
MessageBoxControl surface = new MessageBoxControl(popup);
surface.HeaderContent = title;
surface.Content = text;
surface.DialogStyle = dialogStyle;
surface.Width = 200;
surface.Height = 140;
popup.Child = surface;
popup.IsOpen = true;
return DialogResult.None;
}
}
Updated MessageBoxControl::DialogButtonClick:
void DialogButtonClick(object sender, RoutedEventArgs e) {
DialogButton button = sender as DialogButton;
if (this._owner != null && button != null) {
this._owner.IsOpen = false;
}
}
One thing you will notice with this is that it does not give you the modal feel. In other words, we are still able to click outside of the MessageBox. We can solve this by placing a transparent container that will cover the entire client area before displaying the dialog. A Canvas is the best option in this case as it would allow us to implement window dragging later on.
public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
Popup popup = new Popup();
Canvas backgroundPanel = new Canvas();
// Transparent background for our hidden panel
backgroundPanel.Background = new SolidColorBrush(Colors.Transparent);
// Make it fill up the whole client area
backgroundPanel.Width = Application.Current.Host.Content.ActualWidth;
backgroundPanel.Height = Application.Current.Host.Content.ActualHeight;
MessageBoxControl surface = CreateDialogSurface(title, text, dialogStyle, popup);
backgroundPanel.Children.Add(surface);
CenterControl(surface, backgroundPanel);
popup.Child = backgroundPanel;
popup.IsOpen = true;
return DialogResult.None;
}
private static MessageBoxControl CreateDialogSurface(string title, string text, DialogStyle dialogStyle, Popup popup) {
MessageBoxControl surface = new MessageBoxControl(popup);
surface.HeaderContent = title;
surface.Content = text;
surface.DialogStyle = dialogStyle;
surface.Width = 200;
surface.Height = 140;
surface.Background = new SolidColorBrush(Colors.White);
return surface;
}
private static void CenterControl(FrameworkElement element, Canvas parent) {
if (element.ActualHeight == 0 || element.ActualHeight == double.NaN){
element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
}
element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.ActualHeight / 2 - element.DesiredSize.Height / 2));
element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.ActualWidth / 2 - element.DesiredSize.Width / 2));
}
This is what we have have so far:
Page.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e) {
DevExpressTV.Forms.MessageBox.Show("Title", "Hello World", DevExpressTV.Forms.DialogStyle.Ok);
}
We should probably add a little border for the dialog surface.
generic.xaml:
<Style TargetType="local:MessageBoxControl">
.....
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MessageBoxControl">
<Grid x:Name="RootElement" Background="{TemplateBinding Background}">
<Rectangle Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"></Rectangle>
<Grid Margin="{TemplateBinding BorderThickness}">
.....
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Implementing Window Dragging
This is very simple now that our parent is a Canvas since we can manage the control's position.
public MessageBoxControl(Popup owner)
: this() {
this._owner = owner;
this.MouseLeftButtonDown += new MouseButtonEventHandler(Surface_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Surface_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Surface_MouseMove);
}
#region Dragging
bool _hasNCHitTest = false;
Point _initalNCHitTest = new Point();
void Surface_MouseMove(object sender, MouseEventArgs e) {
if (e.Handled == true)
return;
if (_hasNCHitTest) {
Point hitTest;
hitTest = e.GetPosition(this);
Point delta = new Point(
hitTest.X - _initalNCHitTest.X,
hitTest.Y - _initalNCHitTest.Y);
this.SetValue(Canvas.TopProperty, (double)this.GetValue(Canvas.TopProperty) + delta.Y);
this.SetValue(Canvas.LeftProperty, (double)this.GetValue(Canvas.LeftProperty) + delta.X);
e.Handled = true;
}
}
void Surface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
if (e.Handled == true)
return;
if (_hasNCHitTest) {
_hasNCHitTest = false;
this.ReleaseMouseCapture();
e.Handled = true;
}
}
void Surface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
if (e.Handled == true)
return;
_hasNCHitTest = true;
_initalNCHitTest = e.GetPosition(this);
this.CaptureMouse();
e.Handled = true;
}
#endregion
That's all there is too it. Well kinda :). Remember the declaration for the MessageBox.Show?
public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
....
return DialogResult.None;
}
It assumes that the call to MessageBox.Show will be a blocking one, which is not! So how should we get the DialogResult back? We'll look at this problem in Part III.
UPDATE: To answer James' questions below about the resizing of the background canvas. This can be accomplished by hooking into the Application.Host.Current.Content.Resized event. For example:
public MessageBoxControl(Popup owner, DialogCloseDelegate callback)
: this() {
this._owner = owner;
this._callback = callback;
this.MouseLeftButtonDown += new MouseButtonEventHandler(Surface_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Surface_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Surface_MouseMove);
Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
}
void Content_Resized(object sender, EventArgs e) {
if (_owner != null && _owner.Child != null) {
Panel backgroundPanel = _owner.Child as Panel;
if (backgroundPanel != null) {
backgroundPanel.Width = Application.Current.Host.Content.ActualWidth;
backgroundPanel.Height = Application.Current.Host.Content.ActualHeight;
}
}
}
Cheers
Azret