Building a message box with the Silverlight ChildWindow

15 December 2010 3 comments

In this post I’m going to describe the steps to create a custom message box in Silverlight that has a few more button combinations than ok and cancel and can handle callbacks with result and state data.

To start create a new Silverlight Application project in VS2010 and add a user control called MessageBox.xaml. You end up with some xaml similar to that below.

<UserControl x:Class="SilverlightApplication4.MessageBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>

Next task is to change the base class from a UserControl to a ChildWindow. To make this possible you'll need to add a reference to the assembly containing the child window control which is part of the Silverlight 4 SDK. This is normally located at C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\System.Windows.Controls.dll. Once this has been done a reference to this namespace needs adding to the xaml and the root node updating. This is shown below.

<Controls:ChildWindow x:Class="SilverlightApplication4.MessageBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</Controls:ChildWindow>

Once this is done we need to jump to the code-behind and update the inherited class here from UserControl to ChildWindow, you can actually remove the inherited class altogether as it is specified in the xaml which is a partial class of the code-behind (thanks to ReSharper for pointing that out).

namespace SilverlightApplication4
{
    public partial class MessageBox
    {
        public MessageBox()
        {
            InitializeComponent();
        }
    }
}

So far so good. We now need to add the placeholder for the message box text and the buttons. This is just a matter of adding some simple controls to the grid of the MessageBox control.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <ScrollViewer BorderBrush="{x:Null}"
                  ScrollViewer.VerticalScrollBarVisibility="Auto"
                  Padding="5">
        <TextBlock x:Name="MessageBoxText"
                   TextWrapping="Wrap" />
    </ScrollViewer>
    <StackPanel Grid.Row="1"
                HorizontalAlignment="Right"
                Orientation="Horizontal"
                VerticalAlignment="Center"
                Margin="0,0,5,0">
        <Button x:Name="YesButton"
                Margin="3,0,0,0"
                Content="Yes"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="Yes" />
        <Button x:Name="NoButton"
                Margin="3,0,0,0"
                Content="No"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="No" />
        <Button x:Name="OkButton"
                Margin="3,0,0,0"
                Content="OK"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="Ok" />
        <Button x:Name="AbortButton"
                Margin="3,0,0,0"
                Content="Abort"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="Abort" />
        <Button x:Name="RetryButton"
                Margin="3,0,0,0"
                Content="Retry"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="Retry" />
        <Button x:Name="CancelButton"
                Margin="3,0,0,0"
                Content="Cancel"
                MinWidth="70"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonClick"
                CommandParameter="Cancel" />
    </StackPanel>
</Grid>

You'll notice there's a shared button click event handler called ButtonClick and an associated CommandParameter in each button instance. This is in the code-behind and is used to process the click event for each button. Before we can continue we need to add two enums one used to define the buttons that are shown and the other to define the value returned via the callback when the message box is closed.

public enum MessageBoxButton
{
    YesNo,
    Ok,
    OkCancel,
    AbortRetryCancel
}
public enum MessageBoxResult
{
    Yes,
    No,
    Ok,
    Abort,
    Retry,
    Cancel
}

Once these are added to the project we can build the click handler.

private void ButtonClick(object sender, System.Windows.RoutedEventArgs e)
{
    Close();

    var result = MessageBoxResult.Ok;

    switch ((string)((Button)sender).CommandParameter)
    {
        case "Yes":
            result = MessageBoxResult.Yes;
            break;

         case "No":
            result = MessageBoxResult.No;
            break;

        case "Ok":
            result = MessageBoxResult.Ok;
            break;

        case "Abort":
            result = MessageBoxResult.Abort;
            break;

        case "Retry":
            result = MessageBoxResult.Retry;
            break;

        case "Cancel":
            result = MessageBoxResult.Cancel;
            break;
    }

    if (ResultHandler != null) ResultHandler(result, State);
}

The initial Close() statement calls the inherited method from the ChildWindow to close the window. The switch statement then determines the result and the ResultHandler sends the result back to any handler defined when showing the message box. This we will move on to.

public static void Show(string messageBoxText, string caption = "Message Box", MessageBoxButton button = MessageBoxButton.Ok, Action<MessageBoxResult, object> resultHandler = null, object state = null)
{
    var messageBox = new MessageBox
        {
            MessageBoxText = {Text = messageBoxText},
            Title = caption,
            ResultHandler = resultHandler,
            State = state                                   
        };

    switch (button)
    {
        case MessageBoxButton.YesNo:
            messageBox.YesButton.Visibility = Visibility.Visible;
            messageBox.NoButton.Visibility = Visibility.Visible;
            messageBox.OkButton.Visibility = Visibility.Collapsed;
            messageBox.AbortButton.Visibility = Visibility.Collapsed;
            messageBox.RetryButton.Visibility = Visibility.Collapsed;
            messageBox.CancelButton.Visibility = Visibility.Collapsed;
            break;

        case MessageBoxButton.Ok:
            messageBox.YesButton.Visibility = Visibility.Collapsed;
            messageBox.NoButton.Visibility = Visibility.Collapsed;
            messageBox.OkButton.Visibility = Visibility.Visible;
            messageBox.AbortButton.Visibility = Visibility.Collapsed;
            messageBox.RetryButton.Visibility = Visibility.Collapsed;
            messageBox.CancelButton.Visibility = Visibility.Collapsed;
            break;

        case MessageBoxButton.OkCancel:
            messageBox.YesButton.Visibility = Visibility.Collapsed;
            messageBox.NoButton.Visibility = Visibility.Collapsed;
            messageBox.OkButton.Visibility = Visibility.Visible;
            messageBox.AbortButton.Visibility = Visibility.Collapsed;
            messageBox.RetryButton.Visibility = Visibility.Collapsed;
            messageBox.CancelButton.Visibility = Visibility.Visible;
            break;

        case MessageBoxButton.AbortRetryCancel:
            messageBox.YesButton.Visibility = Visibility.Collapsed;
            messageBox.NoButton.Visibility = Visibility.Collapsed;
            messageBox.OkButton.Visibility = Visibility.Collapsed;
            messageBox.AbortButton.Visibility = Visibility.Visible;
            messageBox.RetryButton.Visibility = Visibility.Visible;
            messageBox.CancelButton.Visibility = Visibility.Visible;
            break;
    }

    messageBox.Show();
}

The Show method is defined as static so that it can be called without having to create an instance of the MessageBox itself. It in turn will create an instance of itself and set it's own properties. The MessageBoxText we defined in the xaml previously and the Title is built in to the ChildWindow. Only the ResultHandler and the State are local properties of the MessageBox.

private Action<MessageBoxResult, object> ResultHandler { get; set; }
private object State { get; set; }

Once we've set the MessageBox properties it's a matter of showing the correct buttons and then finally showing the message box.

That's it. Hope you find this useful and as always there is a zip of a test project here.

Categories: Silverlight

Silverlight BusyIndicator styles

23 October 2010 Leave a comment

I’ve just finished a post about using multiple busy indicators on one view in Silverlight and included a project that contained custom styles for the indicators. There are two styles both meant to be much more simplistic than the default look.

Style 1 – Circular dot rotating clockwise with fading trailing dots.

Style 2 – Linear dot oscillating back and forth with trailing dots reducing in size.

The resource file is available here.

Categories: Silverlight

Multiple Silverlight BusyIndicators and one view

23 October 2010 2 comments

Whilst working on an app I hit a situation where I needed to show multiple busy indicators in the same page rather than one locking the whole thing. Since Silverlight 4 supports indexed properties for binding I decided to add an indexed property of IsBusy to my ViewModelBase. This is the base class that all my view-models use under the MVVM pattern. I am assuming that MVVM is understood here but if not there are loads of articles on the net that do a better job than I could of explaining it.

Below is a stripped down version of my ViewModelBase just showing the IsBusy property and the INotifyPropertyChanged implementation.


using System.Collections.Generic;
using System.ComponentModel;

public class ViewModelBase : INotifyPropertyChanged
{
    protected ViewModelBase()
    {
        IsBusy = new IsBusyIndexer(this);
    }

    public class IsBusyIndexer : Dictionary<string, int>
    {
        private readonly ViewModelBase _owner;

        public IsBusyIndexer(ViewModelBase owner)
        {
            _owner = owner;
        }

        public bool Default
            {
                get { return this[string.Empty]; }
                set { this[string.Empty] = value; }
            }

            new public bool this[string key]
            {
                get
                {
                    return ContainsKey(key) && base[key] > 0;
                }

                set
                {
                    if (value)
                    {
                        if (!ContainsKey(key))
                        {
                            Add(key, 1);
                        }
                        else
                        {
                            base[key]++;
                        }
                    }
                    else if (ContainsKey(key))
                    {
                        if (base[key] == 1)
                        {
                            Remove(key);
                        }
                        else
                        {
                            base[key]--;
                        }
                    }

                    _owner.OnPropertyChanged("IsBusy");
                }
            }
    }

    public IsBusyIndexer IsBusy { get; private set; }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

This setup allows me to define IsBusy items using either the IsBusy.Default (same as [""]) option or the IsBusy["KEY"] approach. These can then be bound in XAML to any BusyIndicator IsBusy property as shown in the following examples.


<toolkit:BusyIndicator IsBusy="{Binding IsBusy.Default}" />

<toolkit:BusyIndicator IsBusy="{Binding IsBusy[Key1]}" />

<toolkit:BusyIndicator IsBusy="{Binding IsBusy[Key2]}" />

Using this approach the indicators can be activated and deactivated by setting the base class property to true or false from within the view's view-model.


IsBusy.Default = true;

IsBusy["Key1"] = true;

IsBusy["Key2"] = true;

That's really it. The only tasks left would be to style the indicators to fit your needs and content.

You can download an example project from here. This includes a simple example of multiple indicators which have also been re-styled.

Categories: MVVM, Silverlight

SQLExpress 2005 to 2008

27 February 2010 Leave a comment

If like me you’ve installed VS08 SP1 and ended up with SQL Express 2005 you may want to update this to 2008. The normal installation route tries to take you down the Web Installer route but this just warns you that 2008 is not compatible with 2005. That’s all you get, no help on what to do or where to go.

So here’s how I got around it based on this blog post Upgrade from SQL Express 2005 to 2008

1. Download SQL Express 2008 from here http://www.microsoft.com/downloads/details.aspx?familyid=01AF61E6-2F63-4291-BCAD-FD500F6027FF&displaylang=en.

2. Extact the contents using this command ‘SQLEXPR_x86_ENU /EXTRACT’.

3. Perform an upgrade using this command ‘SETUP.EXE /QUIET /ACTION=upgrade /INSTANC
ENAME=SQLEXPRESS’.

Be patient it can take a while and appear that not a lot is happening. Mine took about 10 mins. Once your prompt comes back you’re done.

Categories: SQL Express

BizTalk scripting functoids

24 February 2010 Leave a comment

Came across a rather annoying little gotcha in BizTalk the other day. It appears that if you use the scripting functoid and choose C# code as the script run (might be the same for others but I’ve only used the C# option to date) you need to be careful with your function naming.

Initially I had four scripting functoids on the design surface all with the same input. This input was being split on a space char and each one was presenting a different element (index) to the destination schema. I decided to call all the methods in each functoid the same.

public string GetElement(string param1)
{
var param1Elements = string.Split(param1, ‘ ‘);
return param1Elements[0]; // This is the part that changed between each version.
}

It took me a while to realise that even though the mapper inserts each function in the resultant xslt each call was calling the first instance and therefore returning the first element.

Anyway hope that makes sense and helps someone. A total fail on the designers part by not warning you that methods need to be unique.

Categories: BizTalk, Gotcha

Removing .svn

3 November 2009 Leave a comment

There has been occasion where I have had to uncouple my projects from SVN. This in itself is simply a matter of removing the hidden .svn folders. However if you have a deep folder tree this can be not only error prone but boring as hell so to aid the exercise I found the following…

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN]
@=”Delete SVN Folders”

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN\command]
@=”cmd.exe /c \”TITLE Removing SVN Folders in %1 && COLOR 9A && FOR /r \”%1\” %%f IN (.svn) DO RD /s /q \”%%f\” \””

Can’t claim any credit for this as it was Jon Galloway‘s Shell Command – Remove SVN Folders post that saved the day.

Categories: Subversion, SVN, Tips

Synergy

17 September 2009 Leave a comment

If you’re like me and use more than one rig at a time you probably find it a total pain having two keyboards and mice.

I’ve been using this for a while now and it’s proved to be one of most useful things I’ve ever downloaded and it has the added benefit of freaking the odd person out with “how did you…”

Synergy+

Categories: Tools
Follow

Get every new post delivered to your Inbox.