Pixata Custom Controls
For Lightswitch

Recent Posts

Popular tags (# posts in brackets)

Anonymous types (3) ASP.NET (5) C# (3) C# tricks and tips (2) Computers (6) Design patterns (3) DomainDataSource (3) Dynamic data (4) Entity framework (3) Entity model framework (5) F# (3) LightSwitch (12) Linq (6) Microsoft (2) MVP (2) MVVM (2) Project Euler (2) RIA services (5) Silverlight (2) SQL Server (4) Unit testing (4) Visual Studio (7) WCF (3) WPF (3)

Gratuitous link to StackExchange

Archives


Categories


Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Actually, as I'm self-employed, I guess that means that any views I expressed here aren't my own. That's confusing!


Acknowledgments

Theme modified from one by Tom Watts
C#/F# code styling by Manoli (for posts pre-2016) and Google code prettify (for post from Jan 2016 and beyond)


My rambling thoughts on exploring the .NET framework and related technologies

The revolutionary Toast control control Toast

Although familiar to Silverlight developers, this control is what I believe is the very first notification toast implementation for Lightswitch.

The simple usage of the control is to allow you to display a small notification window in the lower right-hand corner of the user's screen (upper right-hand if they are using a Mac), which will not interfere with whatever they are doing, and will quietly go away after a short time. They are officially called "notification windows" but are colloquially known as toast, due to the way they pop up. One of the most famous uses of toast is when Microsoft Outlook informs you that you have a new email. This is a very user-friendly way of doing this, and is far better than the evil message boxes that plagued user interfaces for decades. If you still things message boxes are OK, please read some more about why they are evil and stop using them!

Imagine you were writing an application that maintained a list of ferrets (well, don't we all?), and you wanted to notify the user when a new ferret had been added to the database. Instead of annoying them with a message box, you could simply pop up a notification like this...

A notification toast

This has the huge advantage that it is not modal, meaning that they user can continue working, without having to stop what they are doing, click the OK button to get rid of the message box (which they didn't read anyway as they had their minds on other things), and then sit there wondering what they were doing before they were so rudely interrupted. With the unobtrusive Toast, the user can either ignore it, or click the close button to dismiss it. If they ignore it, it will fade out after about 4 seconds.

I thought it would be really nice if we could have these in Lightswitch, and developed a control to make it easy.

Before I continue though, it's worth pointing out that due to a limitation in the Silverlight API, toasts will only work in Out-Of-Browser applications. If you attempt to use this control in a browser, nothing will happen. Also, according to the documentation, you can't have any interactive controls on the toast, although it was quite easy to add the close button (look at the top-right of the toast in the picture above), so I'm not sure that this is as restrictive as it sounds. Finally, the API does not give any control of the fade in/out duration, the shape of the toast (so no rounded corners), or the way the toast appears (eg sliding in as opposed to fading in), so we are stuck with the default behaviours in these respects. Having said that, there's still a lot you can do with them, as I hope to show. With that aside, let's see how to use the control.

Displaying Toast

All you need to do to display toasts is to switch to the code behind of the screen where you want the notification to appear. At the top of the file, add the following line...

using PixataCustomControls.Presentation.Controls;

You don't need to worry about adding a reference to the controls, if you've enabled them in the Lightswitch project properties, then the reference will already be there.

To display the toast, you can do something as simple as this...

  PixataToastHelper.ShowToast("Title", "Type your message here");

This will display a fairly plain toast, using the title and message you supplied. Obviously, you can modify these to suit your needs, including data from the Lightswitch application.

If you want something a little more interesting, you can customise the toast by adding the following parameters to the call to ShowToast...

Parameter Meaning
ToastStyle There is an enumeration in the control that gives you five more styles for the toast. These are Info, Warning, Alert, Error and Elephant. The more observant readers may notice that the last one is a little out of place. This is because I was creating a custom style to show how easy it is to add your own (see below), and decided to leave it in as it amused me.

If you change the call to the following, you'll end up with a toast that looks like the one shown above...

  PixataToastHelper.ShowToast("Ferret Information", "New ferret added 6th March '12, by user Freddy",
    PixataToastHelper.PixataToastStyles.Info);
Notice that last parameter? That sets one of the six predefined styles (including the default Plain style), and will be suitable for most purposes.
TimeOut This integer value specifies for how milliseconds the toast will be displayed before it disappears.

Due to limitations in the .NET API, this value cannot be less than zero (I believe Microsoft are working on time travel, but I don't think it will be available until .NET 5 comes out), nor greater than 30000. Any values outside of this range will be modified to be between zero and 30000.
Width and Height These are pretty self-explanatory. Again, due to API limitations, toasts cannot be wider than 400 pixels, nor higher than 100 pixels. If you enter a value less than zero or greater than these maximum values, it will be modified.

That's basically all there is to the standard usage. As you can see, very easy to use, but very effective.

Note that as of version 1.12, toasts now queue, so if you make a call to display a toast before a previous one has been dismissed 9or faded away), the new one will wait politely for its turn, instead of barging in and shoving the old one out of the way.

However, you might look at these and say "I don't like your designs, I want it different." Well, that's absolutely fine, because I added the capability for you to create your own toast styles! It's a little more involved than the standard usage, but not much.

Creating Your Own Toast

The first thing you'll need to do is to add a new Silverlight User Control to your Client project. For the purposes of this blog post, I'm going to show you how I built the Elephant style. Truth is, the only reason that style ended up in the extension is because once I had built it, I liked it enough to keep it! Imagine you have called your control MyToast.

As an aside, I have no idea why you would use such a dumb name. I simply cannot fathom why Microsoft think that we all want our folders called My Documents, My Saved Navel Fluff, My Viruses and so on. Anyway, I had a senior moment and called this sample control MyToast, so you're going to have to live with that for this tutorial.

You will need to make a few changes to the control before you start designing your toast. Without boring you with the details, any toast control needs to be derived from a base control I wrote for the purpose. This allows you to pass the toast into the ShowToast method. At the top of the XAML, add a reference to the Pixata Custom Controls namespace, and then change the top-level tag from UserControl to uc:PixataToastControlBase as follows...

<uc:PixataToastControlBase x:Class="LightSwitchApplication.MyToast"
  xmlns:uc="clr-namespace:PixataCustomControls.Presentation.Controls;assembly=PixataCustomControls.Client"

I also like to add a design width and height, as these make it easier to work with when you're designing the toast. The end of your opening tag should look like this...

  mc:Ignorable="d"
  d:DesignHeight="100"
  d:DesignWidth="400">

Now switch to the control's code behind, and have the control inherit from PixataToastControlBase instead of UserControl. You'll need to add the following using at the top of the file...

using PixataCustomControls.Presentation.Controls;

...and then change the class declaration as follows...


public partial class MyToast : PixataToastControlBase {

At this stage, you can now happily design your toast. If you want to play along, here is the XAML for my elephant control...

<uc:PixataToastControlBase x:Class="LightSwitchApplication.MyToast"
                           xmlns:uc="clr-namespace:PixataCustomControls.Presentation.Controls;assembly=PixataCustomControls.Client"
                           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="100"
                           d:DesignWidth="400">
 

<Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="100" />
    </Grid.ColumnDefinitions>
    <StackPanel Orientation="Vertical"
                Background="Black">
      <TextBlock Name="Title"
                 Text="The Title"
                 Margin="2,0"
                 HorizontalAlignment="Center"
                 FontWeight="Bold"
                 FontSize="14"
                 Foreground="White" />
      <Rectangle HorizontalAlignment="Stretch"
                 VerticalAlignment="Top"
                 Margin="5,0"
                 Fill="#FFC4C4C4"
                 Height="1" />
      <TextBlock Name="Message"
                 Text="The stuff on the toast that blathers on about nothing"
                 Margin="5,0"
                 VerticalAlignment="Top"
                 HorizontalAlignment="Left"
                 Foreground="White"
                 TextWrapping="Wrap" />
    </StackPanel>
    <Button Grid.Column="1"
            Padding="0"
            Click="Button_Click">
      <Button.Content>
        <Image Width="100"
               Height="100"
               Source="/TestingPixataWithFerrets.Client;component/Images/elephant100x100.png" />
      </Button.Content>
    </Button>

</Grid>
</uc:PixataToastControlBase>

Obviously, you don't have to add a picture of an elephant, but it won't look as nice if you don't! I found this one from www.findicons.com. Here's how it looked in the Visual Studio designer...

The elephant toast in the VS designer

Notice that I've included a button in the XAML, and wired up the Click event. This allows the user to dismiss the toast before it times out. In this instance, the button is disguised as an elephant, which isn't actually very user friendly. The Elephant toast style that comes with the control has a normal close button in the top right-hand corner of the toast, which is generally a better idea. People just aren't used to the idea of clicking elephants yet!

Now, in order to get the title and message into your controls, you can override two string properties from the base class (actually from an interface that the base class implements, but let's not split hairs)...

    public override string TitleText {
      set {
        Title.Text = value;
      }
    }
 
    public override string MessageText {
      set {
        Message.Text = value;
      }
    }

In this case, Title and Message are the names of the two TextBlock controls I put in the XAML. If you name yours differently, just modify this code.

The only other thing left to do is add a handler for the button's Click event, which is used to allow the user to close the toast. Put the following code right below the two properties...

    private void Button_Click(object sender, RoutedEventArgs e) {
      CloseMe();
    }

CloseMe is a method in the base toast class that you inherited earlier, so you don't need to write any more code than this.

Using The Toast In Lightswitch

Now you are ready to test your control. In your Lightswitch screen's code behind, use the second overload of the ShowToast method, that takes your new toast control instead of a value from the enumeration. Note that as you are creating a control, you need to dispatch to the main thread, to avoid access violation exceptions. When using the built-in toast styles, you don't need to worry about this as the control does it for you...

  Dispatchers.Main.BeginInvoke(() => {
    PixataToastHelper.ShowToast("Elephants!", "Watch out for the elephants!", new MyToast(), Width: 200, Height: 100);
  });

I have overridden the default width and height values here, and the result looks like this...

A notification toast

If you leave it alone, the toast will disappear after 4 seconds (as we didn't override the default), or you can click the elephant and the toast will disappear immediately.

As mentioned above, it would be more intuitive to have the button look like a button, but I'll leave that for you as an exercise. Due to the fact that I grew rather fond of him, a slightly modified version of the elephant ended up in the extension, and was used as the control icon.

More Advanced Toast Design

So far, we have seen basic toast design. With only a little imagination, you should be able to tweak this little toasties to do almost anything!

The limitation with the method I showed above is that it only allows you to set the title and message for the toast, and each of these has to be sent as plain text, without any formatting. In many cases, this is fine, but sometimes you want more control. The important thing to remember is that when you create your own toast, there is nothing to stop you from doing what you like with it before passing it to the ShowToast method. You can create a toast with any properties you like, populate those in your Lightswitch screen's code-behind, and when you are done, pass it in to ShowToast. You are not limited to passing "new MyToast()" like I showed yesterday.

As an example, I'm going to assume you want to show a pop-up with some RSS feed information in it. I realise that this isn't very likely from Lightswitch, but it was the first thing that sprang into my mind, partly inspired by finding a nice RSS icon on www.findicons.com. I called the new toast control ToastWithPeanutButter, mainly because I have a very childish sense of humour!

Create the basic control by following the instructions above. Now, instead of restricting ourselves to two TextBlock controls for the title and message, we are going to do something a little more interesting for the message. Open up the ToastWithPeanutButter.xaml file, and replace the content with the following...

<uc:PixataToastControlBase x:Class="LightSwitchApplication.ToastWithPeanutButter"
                           xmlns:uc="clr-namespace:PixataCustomControls.Presentation.Controls;assembly=PixataCustomControls.Client"
                           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="100"
                           d:DesignWidth="400">
 

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <!-- Title bar -->
    <Grid HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>
      <Grid.Background>
        <LinearGradientBrush EndPoint="0.5,1"
                             StartPoint="0.5,0">
          <GradientStop Color="#FFE3EFF7"
                        Offset="0" />
          <GradientStop Color="#FFD6E7F3"
                        Offset="1" />
        </LinearGradientBrush>
      </Grid.Background>
      <TextBlock Name="TitleTb"
                 HorizontalAlignment="Center"
                 Text="Title"
                 FontWeight="Bold" />
      <Button VerticalAlignment="Center"
              HorizontalAlignment="Right"
              Grid.Column="1"
              Margin="5,3"
              Width="14"
              Height="14"
              Padding="0"
              Click="Button_Click">
        <Button.Content>
          <Image Source="/TestingPixataWithFerrets.Client;component/Images/closebutton.png"
                 Width="14"
                 Height="14"
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center" />
        </Button.Content>
      </Button>
    </Grid>
    <!-- Main content area -->
    <Grid Grid.Row="1">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <!-- tea cup RSS icon -->
      <Image Width="64"
             Height="64"
             Margin="8"
             VerticalAlignment="Center"
             HorizontalAlignment="Center"
             Source="/TestingPixataWithFerrets.Client;component/Images/rss.png" />
      <TextBlock Name="MainContentTb"
                 Grid.Column="1"
                 Margin="3"
                 Text="Main content" />
    </Grid>
    <Grid.Background>
      <LinearGradientBrush EndPoint="0.5,1"
                           StartPoint="0.5,0">
        <GradientStop Color="#FFFF9B00"
                      Offset="0" />
        <GradientStop Color="#FFF8D183"
                      Offset="1" />
      </LinearGradientBrush>
    </Grid.Background>

</Grid>
</uc:PixataToastControlBase>

If you look at this in the Visual Studio designer, it will look like this...

The new toast in the VS designer

Looks pretty much like the previous ones eh? It is, but we are going to do one thing differently, and that will make a lot of difference.

Switch to the code behind, and override the TitleText property as before, as we are using a plain string for the title. However, instead of overriding the MessageText property, we are going to expose the MainContent TextBlock control, so that the calling code can manipulate it. Change the code to look like this...

using System.Windows;
using System.Windows.Controls;
using PixataCustomControls.Presentation.Controls;
 
namespace LightSwitchApplication {

public partial class ToastWithPeanutButter : PixataToastControlBase {
    public ToastWithPeanutButter() {
      InitializeComponent();
    }
 
    public override string TitleText {
      set {
        TitleTb.Text = value;
      }
    }
 
    public TextBlock MainContent {
      get {
        return MainContentTb;
      }
    }
 
    private void Button_Click(object sender, RoutedEventArgs e) {
      CloseMe();
    }
  }
}

Notice that we don't override the MessageText property, so anything we pass in for that parameter will not be used.

Our new toast is complete, let's see how we can use it. In a Lightswitch screen code-behind, add the following...

Dispatchers.Main.BeginInvoke(() => {
  ToastWithPeanutButter toast = new ToastWithPeanutButter();
  TextBlock tb = toast.MainContent;
  tb.Text = "";
  tb.Inlines.Add(CreateRun("DotNetWhatNot - ", Bold: true));
  tb.Inlines.Add(CreateRun("7th Mar '12 16:42"));
  tb.Inlines.Add(new LineBreak());
  tb.Inlines.Add(CreateRun("Toast control added to Pixata Custom Controls for Lightswitch", ForeGround: Colors.Brown));
  tb.Inlines.Add(new LineBreak());
  tb.Inlines.Add(CreateRun("Some MSDN Blog - ", Bold: true));
  tb.Inlines.Add(CreateRun("7th Mar '12 17:31"));
  tb.Inlines.Add(new LineBreak());
  tb.Inlines.Add(CreateRun("Microsoft announce new version of VS - no Lightswitch!!", ForeGround: Colors.Brown));
  PixataToastHelper.ShowToast("Latest RSS feeds", "", toast, Width: 400, Height: 100);
});

For convenience, this uses the following utility method...

private Run CreateRun(string Text, int FontSize = 11, Color? ForeGround = null, bool Bold = false) {
  Run r = new Run {
    Text = Text,
    FontSize = FontSize,
    Foreground = (ForeGround == null ? new SolidColorBrush(Colors.Black) : new SolidColorBrush((Color)ForeGround)),
    FontWeight = (Bold ? FontWeights.Bold : FontWeights.Normal)
  };

return r;
}

This utilises a little-known feature of the TextBlock control, that you can add formatted multiline text to it by manipulating the Inlines collection, which can take Run and LineBreak objects. Using this utility method, we build up a list of two RSS items, which in reality would be pulled from your data source. I hard-coded the information to keep the example simple.

The result of this is the following toast..

The new toast

That's a definite improvement over yesterday's toast!

Obviously, you don't need to do all that work creating Run and LineBreak objects if you don't want, you can just add more controls to the toast's XAML, and expose their Text (or other) properties from the toast control. That way, the code you write in Lightswitch can set up the toast as you want before passing it to the ShowToast method.

I hope this post has whetted your appetite for creating more interesting toasts. If you have a toast that you feel would be useful to others, or just want to share an interesting creation, please let me know.

Note: The contents of this page are slightly modified from two blog posts I made when I released the control. You can read the original announcement, as well as the follow-up post (in which I explained how to create more advanced toasts).