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

The One With

Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part I)

There is no support for modal dialogs in Silverlight 2.0 and there is no quick way to say MessageBox.Show("Hello World"). There is a Popup "primitive" however that can be used to create such things.

For example

Page.xaml:

<Popup x:Name="myMessageBox">
<Grid Width="400" Height="150">
<Rectangle Stroke="Black" StrokeThickness="1"/>
<Grid Margin="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" x:Name="HeaderElement" Background="Navy">
<TextBlock Text="Message" Foreground="White"></TextBlock>
</Grid>
<Grid Grid.Row="1" x:Name="ContentElement" VerticalAlignment="Stretch">
<TextBlock Text="Hello World"
VerticalAlignment="Center"
HorizontalAlignment="Center"></TextBlock>
</Grid>
<Grid Grid.Row="2" x:Name="ButtonsPanelElement" Margin="2">
<StackPanel Margin="4">
<Button Content="OK" Width="75" Click="PopupClose"/>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Popup>

Page.xaml.cs:

private void ShowMessageBox(object sender, RoutedEventArgs e) {
myMessageBox.HorizontalOffset = 100;
myMessageBox.VerticalOffset = 100;
myMessageBox.IsOpen = true;
}

private void PopupClose(object sender, RoutedEventArgs e) {
myMessageBox.IsOpen = false;
}
 

 

We are still missing support for dragging, "modality" and the ability to customize and reuse this control. Implementing all of this inside the application itself quickly leads to unreadable and unmaintainable code. Fortunately,  it is easy enough to create a reusable library that will wrap all this plumbing. This is what we want:

public enum DialogResult {
None,
Ok,
Cancel,
}

public enum DialogStyle {
Ok,
OkAndCancel,
}

public static class MessageBox {
public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
// TODO:
}
}

Part I: Creating a MessageBox content presenter

In the newly created Silverlight Class Library (DevExpressTV.Forms.dll), here is our dummy MessageBoxControl.

MessageBox.xaml.cs:

public class MessageBoxControl : ContentControl {
public MessageBoxControl() {
DefaultStyleKey = typeof(MessageBoxControl);
}
}

generic.xaml:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DevExpressTV.Forms"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Style TargetType="local:MessageBoxControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MessageBoxControl">
<Grid x:Name="RootElement" Background="{TemplateBinding Background}">
<ContentPresenter
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Adding Header Support

MessageBox.xaml.cs::MessageBoxControl:

public static readonly DependencyProperty HeaderContentProperty =
DependencyProperty.Register("HeaderContent", typeof(object), typeof(MessageBoxControl), null);

public object HeaderContent {
get { return (object)GetValue(HeaderContentProperty); }
set { SetValue(HeaderContentProperty, value); }
}

public static readonly DependencyProperty HeaderForegroundProperty =
DependencyProperty.Register("HeaderForeground", typeof(Brush), typeof(MessageBoxControl), null);

public Brush HeaderForeground {
get { return (Brush)GetValue(HeaderForegroundProperty); }
set { SetValue(HeaderForegroundProperty, value); }
}

public static readonly DependencyProperty HeaderBackgroundProperty =
DependencyProperty.Register("HeaderBackground", typeof(Brush), typeof(MessageBoxControl), null);

public Brush HeaderBackground {
get { return (Brush)GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); }
}

generic.xaml:

<Style TargetType="local:MessageBoxControl">
<Setter Property="HeaderBackground" Value="Navy"></Setter>
<Setter Property="HeaderForeground" Value="White"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MessageBoxControl">
<Grid x:Name="RootElement" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid
Background="{TemplateBinding HeaderBackground}"
x:Name="HeaderRootElement">
<ContentPresenter
Foreground="{TemplateBinding HeaderForeground}"
Margin="2,0,0,0"
x:Name="HeaderPresenterElement"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
Content="{TemplateBinding HeaderContent}"/>
</Grid>
<ContentPresenter
x:Name="ContentPresenterElement"
Grid.Row="1"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Page.xaml:

<DevExTV:MessageBoxControl Background="AliceBlue" 
HeaderContent="MessageBox" Width="300" Height="150" Content="Hello World">
</DevExTV:MessageBoxControl>

 

Creating Dialog Buttons

public static readonly DependencyProperty DialogStyleProperty =
DependencyProperty.Register("DialogStyle", typeof(DialogStyle), typeof(MessageBoxControl),
new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MessageBoxControl c = d as MessageBoxControl;
if (c != null) {
c.CreateDialogButtons();
}
}));

public DialogStyle DialogStyle {
get { return (DialogStyle)GetValue(DialogStyleProperty); }
set { SetValue(DialogStyleProperty, value); }
}

The number of Dialog buttons that we want displayed changes depending on the DialogStyle property. So we will need to create them at runtime and place them inside some container panel. To save time, I will use the StackPanel with the Orientation set to "Horizontal" as the container for the dialog buttons.

Updated Control Template in generic.xaml:

<ControlTemplate TargetType="local:MessageBoxControl">
<Grid x:Name="RootElement" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid
Background="{TemplateBinding HeaderBackground}"
x:Name="HeaderRootElement">
<ContentPresenter
Foreground="{TemplateBinding HeaderForeground}"
Margin="2,0,0,0"
x:Name="HeaderPresenterElement"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
Content="{TemplateBinding HeaderContent}"/>
</Grid>
<ContentPresenter
x:Name="ContentPresenterElement"
Grid.Row="1"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Grid Grid.Row="2" Margin="2,2,2,8">
<StackPanel x:Name="ButtonsPanel"
Orientation="Horizontal"
HorizontalAlignment="Center"/>
</Grid>
</Grid>
</Grid>
</ControlTemplate>

CreateDialogButtons: For simplicity, I hard-coded the content for the OK and Cancel buttons.

void CreateDialogButtons() {
Panel panel = this.ButtonsPanel;
if (panel != null) {
panel.Children.Clear();
switch (this.DialogStyle) {
case DialogStyle.Ok:
CreateButton("OK", this.ButtonTemplate, panel, DialogResult.Ok);
break;
case DialogStyle.OkAndCancel:
CreateButton("OK", this.ButtonTemplate, panel, DialogResult.Ok);
CreateButton("Cancel", this.ButtonTemplate, panel, DialogResult.Cancel);
break;
default:
throw new ArgumentOutOfRangeException("DialogStyle");
}
}
}

We call the CreateDialogButtons from 2 places. First when the template is applied to the control:

public override void OnApplyTemplate() {
base.OnApplyTemplate();
this._buttonsPanel = GetTemplateChild("ButtonsPanel") as Panel;
this.CreateDialogButtons();
}

And second when any property that controls a dialog button changes. For example (DefaultStyle, ButtonTemplate). You might have noticed this handler in the DialogStylePoprerty above.

delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MessageBoxControl c = d as MessageBoxControl;
if (c != null) {
c.CreateDialogButtons();
}

We will do the same for the ButtonTemplate property. We expose this property for cases when we want to customize how a dialog button looks.

public static readonly DependencyProperty ButtonTemplateProperty =
DependencyProperty.Register("ButtonTemplate", typeof(ControlTemplate),
typeof(MessageBoxControl), new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MessageBoxControl c = d as MessageBoxControl;
if (c != null) {
c.CreateDialogButtons();
}
}));

public ControlTemplate ButtonTemplate {
get { return (ControlTemplate)GetValue(ButtonTemplateProperty); }
set { SetValue(ButtonTemplateProperty, value); }
}

The code that actually creates a button: (Simplified for clarity)

public class DialogButton : Button {
DialogResult _dialogResult;
public DialogButton(DialogResult dialogResult) {
this._dialogResult = dialogResult;
}
}

void CreateButton(object content, ControlTemplate template, Panel owner,
DialogResult dialogResult) {

DialogButton button = new DialogButton(dialogResult);
button.Content = content;
button.Width = 75;
button.Margin = new Thickness(2, 0, 0, 0);
if (template != null) {
button.Template = template;
}
button.Click += new RoutedEventHandler(DialogButtonClick);
owner.Children.Add(button);
}

void DialogButtonClick(object sender, RoutedEventArgs e) {
}

So far so good.

<DevExTV:MessageBoxControl Background="AliceBlue" 
HeaderContent="MessageBox"
Width="300" Height="150" Content="Hello World"
DialogStyle="OkAndCancel">
</DevExTV:MessageBoxControl>

we can verify that our ButtonTemplate is working as well:

<UserControl.Resources>
<ControlTemplate x:Key="CustomButtonTemplate">
<Grid Background="Red">
<ContentControl
FontWeight="Bold"
Content="{TemplateBinding Content}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"/>
</Grid>
</ControlTemplate>
</UserControl.Resources>
 
<Grid x:Name="LayoutRoot" Background="White">
<DevExTV:MessageBoxControl Background="AliceBlue"
HeaderContent="MessageBox"
Width="300" Height="150" Content="Hello World"
DialogStyle="OkAndCancel"
ButtonTemplate="{StaticResource CustomButtonTemplate}">
</DevExTV:MessageBoxControl>
</Grid>

 

OK, now that we have the basics for the MessageBox content presenter implemented, we can start putting our MessageBox.Show together. (Part II)

Cheers,

Azret

 

PS: If you are following along, drop me quick email about something cool that you would like me to cover or implement as part of this article.

Published Aug 06 2008, 01:37 PM by Azret Botash (Developer Express)
Filed under: ,
Technorati tags: Silverlight, Samples

Comments

 

Linton said:

"...in Silverlight 2.0 and there is no quick way to say MessageBox.Show("Hello World")."

Amazing...we finally reached one-line of code to make a program say "Hello World"...and now we've devolved back to one-thousand lines to do the exact same thing. Silverlight looks like not fun.

Nice post though...

August 6, 2008 7:10 PM
 

Bas Bakker said:

And what is the advantage of doing this with Silverlight rather than with ASP.NET--where it is sooooooooooo much simpler?

August 7, 2008 10:56 AM
 

The One With said:

We have created the basics for the MessageBox dialog in Part I and Part II . One thing that we realized

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
 

Rodolfo said:

Great ..., very nice, i dont like very much xaml ...

jeje cause im from the old school jeje i like create everything with code :) .. but anyway great and useful usercontrol

sorry my bad english

August 18, 2008 10:24 PM
 

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

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

September 10, 2008 7:13 PM

Leave a Comment

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