in
Forums
Blogs
DevExpress.com
Client Center
Support Center
DevExpress Channel

The One With

Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part II - The Popup)

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

Published Aug 06 2008, 04:46 PM by Azret Botash (Developer Express)
Filed under:
Technorati tags: Silverlight

Comments

 

Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part III) - The One With said:

Pingback from  Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part III) - The One With

August 7, 2008 6:25 PM
 

Community Blogs said:

Mark Monster on DynamicProxy and Function Pointers, Emil Stoychev with OlympicShow, Denislav Savkov with

August 9, 2008 7:27 PM
 

Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part III) | My Personal Portfolio said:

Pingback from  Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part III) | My Personal Portfolio

August 14, 2008 7:00 AM
 

James said:

Hi,

Thanks for this it has helped me alot. However I have run in to a problem. If you resize the browser while the popup is displayed the background canvas isn't updating properly (I changed the background to red from transparent to see this). The event for resizing is fireing and the values are correct but it doesn't seem to re-render the canvas. I tried an UpdateLayout but to no avail.

Any ideas on how to fix this?

Cheers

September 10, 2008 11:09 AM
 

James said:

Sorry, one more thing.

If you press enter when the dialog is displayed you get another dialog up (i.e the focus is still on the button behind).

September 10, 2008 11:12 AM
 

Azret Botash (Developer Express) said:

Hello James,

I have updated the post to show you how to resize the background canvas when the browser is resized...

Cheers

Azret

September 10, 2008 7:14 PM
 

Azret Botash (Developer Express) said:

Hello James,

You can set focus to the first button by hooking into the popup.Opened event (right before) the popup.IsOpen = true;

When you handle the event, you should iterate through the children to find the button and call control.Focus() on it

Thanks

Azret

September 10, 2008 7:25 PM
 

James said:

Azret,

Hi, sorry I didn't really make myself clear re the resize. I had allready tried your suggestion (albeit with slight hackier code) however although the event is firing and the code seems to be setting the panel height and width it does not actually resize the panel visually? Is this a bug with silverlight or a nuance with the popup panel?

Thanks for your speedy response.

James

September 11, 2008 5:03 AM
 

Azret Botash (Developer Express) said:

Hello James,

Please fwd me your sample project that does not resize the background panel... I do not think it is a Silverlight bug because it works as expected in all my tests..

Thanks

Azret

September 11, 2008 6:59 PM

Leave a Comment

(required)  
(optional)
(required)  
Verification code: Required
   
Add
Copyright © 1998-2010 Developer Express Inc.
ALL RIGHTS RESERVED