MIX UK and TechEd Europe 2008

Following up on this "I will be at PDC" post, I thought I'd do another "Where will I be" style post and say that I'll also be at MIX UK where I'm doing a Silverlight 2 session;

It looks like I'm on the agenda on the second day and I'll be doing a talk called "No Silverlight (2) Application Is an Island (of richness)".

This is a bit of a deliberately cheesy title in that we kept coming across this "Island of Richness" term used in conjunction with Silverlight and we found it funny in the team but the talk is really about how Silverlight applications can reach out to talk to;

  • The HTML DOM and Javascript
  • The local machine
  • The network via HTTP, SOAP and RESTful web services and sockets

in order to connect to the world beyond the plug-in.

Unfortunately, I won't be at TechEd Europe. I went for the first time last year and my sessions went down pretty well and got used on the front page here;

image

but I must have upset the TechEd people as I didn't get invited back this year :-) Maybe next time?


Filed Under:

PDC 2008

It looks like ( fingers crossed ) I will be able to attend PDC in Los Angeles at the end of October :-)

I'm hoping to be at the pre-con on the Sunday that's focused on concurrent programming and then to be around for the week until the conference ends on Thursday.

I've spoken to a few UK developers in the last couple of weeks who have said that they're going out to PDC and that it'd be good to be able to meet up with other UK folks whilst they're there.

I guess one of those fancy, modern social network type web sites might be more than a bit useful for co-ordinating that?

Has anyone already set something up for UK people at the PDC? I did a quick search but didn't come across anything obvious. I found a facebook site but there's not much about geography on that as far as I can see.

In the meantime, I set up a blog post here as a starting point.


Filed Under:

The Curious Incident of the MessageBox in the Silverlight App

I've been puzzling over how you'd actually do;

MessageBox.Show()

in a Silverlight 2 application. I don't mean HtmPage.Window.Alert(""). I mean something that stays within the bounds of the SL control but asks the user a question. Naturally, there's many ways of doing this without a MessageBox class but it provided me something "interesting" to play with on the train.

The API isn't part of the Silverlight framework so you need to do something yourself.

I'd want MessageBox to follow the traditional model as much as possible. That is, what I want to be able to do is just come along and do;

MessageBox.Show()

and I expect that to "just work" without me having to define anything in XAML or anything like that. It should "just work".

However, I'd also like to be able to customise what the message box displayed actually looks like. That is, I'd like it to be a templated control in many ways but without having to define an instance of a control - a bit of a dilemma!

I want my MessageBox to appear "in front" of all other UI in my application, be modal, offer the user a Yes/No/Ok/Cancel/etc choice of buttons.

How do I get some UI "in front" of all the other parts of my UI without having to pre-define (in XAML or code) the fact that the application might be needing a MessageBox at some point?

The route I took was to attempt to replace the Application.Current.RootVisual. Rather than actually replacing it, I want my UI to appear in front of the RootVisual so I need to somehow take an approach something like;

  1. Grab the root visual
  2. Create some kind of panel.
  3. Disable hit testing on that root visual.
  4. Add the root visual to the background of that panel.
  5. Add my "message box UI" in front of that background.
  6. Set the root visual to be my grid.
  7. Wait for a button click.
  8. Reverse the process.

I made an assumption that the Application.Current.RootVisual would be a UserControl or derived from one. Then, I wanted to get the Content of that UserControl and replace it with my grid ( temporarily ). Now, t seems that UserControl.ContentProperty is a protected property of User Control so I ended up writing a bit of a hack like this that I'm not proud of. Perhaps there's a better way?

  public class UserControlContentAccessor : UserControl
  {
    public static UIElement GetContent(UserControl uc)
    {
      return ((UIElement)uc.GetValue(UserControl.ContentProperty));
    }
    public static void SetContent(UserControl uc, UIElement element)
    {
      uc.SetValue(UserControl.ContentProperty, element);
    }
  }

That class only exists to derive from UserControl so that it can get at its ContentProperty. Not nice.

However, once I'd got over that I sketched out a "form" of MessageBox.Show() with a hard-coded UI just to see what it might look like. In this version, the MessageBox is just embedded as a resource into my application's DLL so that I can load it up. The XAML embedded looks like this;

<?xml version="1.0" encoding="utf-8" ?>
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Width="680.8"
  Height="290.4">
  <Border
    HorizontalAlignment="Stretch"
    Margin="10,10,10,10"
    VerticalAlignment="Stretch"
    BorderThickness="10,10,10,10"
    CornerRadius="3,3,3,3">
    <Border.BorderBrush>
      <LinearGradientBrush
        EndPoint="0.5,1"
        StartPoint="0.5,0">
        <GradientStop
          Color="#06000000" />
        <GradientStop
          Color="#14000000"
          Offset="0.628" />
      </LinearGradientBrush>
    </Border.BorderBrush>
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition
          Height="0.124*" />
        <RowDefinition
          Height="0.522*" />
        <RowDefinition
          Height="0.235*" />
        <RowDefinition
          Height="0.119*" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition
          Width="0.048*" />
        <ColumnDefinition
          Width="0.905*" />
        <ColumnDefinition
          Width="0.047*" />
      </Grid.ColumnDefinitions>
      <Rectangle
        Height="Auto"
        Width="Auto"
        RadiusX="8.4"
        RadiusY="8.4"
        Margin="0,0,0,0"
        Grid.ColumnSpan="3"
        Grid.RowSpan="4">
        <Rectangle.Stroke>
          <LinearGradientBrush
            EndPoint="0.5,1"
            StartPoint="0.5,0">
            <GradientStop
              Color="#B3FFFFFF" />
            <GradientStop
              Color="#B4FFFFFF"
              Offset="1" />
            <GradientStop
              Color="#FFFBF7F7"
              Offset="0.54500001668930054" />
          </LinearGradientBrush>
        </Rectangle.Stroke>
        <Rectangle.Fill>
          <LinearGradientBrush
            EndPoint="1.1599999666214,1.22000002861023"
            StartPoint="-0.152999997138977,-0.33899998664856">
            <GradientStop
              Color="#FFFFFFFF" />
            <GradientStop
              Color="#8CFFFFFF"
              Offset="1" />
            <GradientStop
              Color="#4CFFFFFF"
              Offset="0.339" />
            <GradientStop
              Color="#32FFFFFF"
              Offset="0.63599997758865356" />
            <GradientStop
              Color="#7AFFFFFF"
              Offset="0.81400001049041748" />
            <GradientStop
              Color="#04FFFFFF"
              Offset="0.93300002813339233" />
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <Rectangle
        Height="Auto"
        HorizontalAlignment="Stretch"
        Margin="-1,-1,-0.200000002980232,0.200000002980232"
        VerticalAlignment="Stretch"
        Width="Auto"
        Grid.Column="1"
        Grid.Row="1"
        Grid.RowSpan="2"
        Fill="#FFFFFFFF"
        Stroke="#FF000000" />
      <TextBlock
        Height="Auto"
        HorizontalAlignment="Stretch"
        Margin="20,20,20,20"
        VerticalAlignment="Stretch"
        Width="Auto"
        Grid.Column="1"
        Grid.ColumnSpan="1"
        Grid.Row="1"
        Grid.RowSpan="1"
        FontFamily="Trebuchet MS"
        FontSize="18"
        Text="This is a message inside of a MessageBox!"
        TextWrapping="Wrap" />
      <Rectangle
        HorizontalAlignment="Stretch"
        Margin="1,0,1,1"
        Grid.RowSpan="1"
        Fill="#FFD3D3D3"
        Grid.ColumnSpan="1"
        Grid.Row="2"
        Grid.Column="1" />
      <StackPanel
        HorizontalAlignment="Right"
        Margin="0,0,10,0"
        Grid.RowSpan="1"
        Grid.ColumnSpan="1"
        Grid.Row="2"
        Grid.Column="1"
        Orientation="Horizontal">
        <Button
          Content="OK"
          Width="124.566"
          Height="40.844"
          Margin="5,0,0,0"
          HorizontalAlignment="Right"
          x:Name="buttonOk"/>
        <Button
          Height="40.844"
          Width="124.566"
          Content="Cancel"
          Margin="5,0,0,0"
          HorizontalAlignment="Right" 
          x:Name="buttonCancel"/>
      </StackPanel>
    </Grid>
  </Border>
</Grid>

and then the tentative MessageBox code to show/hide this looks like this ( note my app is called SilverlightApplication16 );

  public static class MessageBox
  {
    private static UIElement realVisual;
    private static Grid parentGrid;

    public static void Show(string text)
    {
      UserControl uc = Application.Current.RootVisual as UserControl;

      if (uc != null)
      {
        realVisual = UserControlContentAccessor.GetContent(uc);
        realVisual.IsHitTestVisible = false;

        parentGrid = new Grid();

        UserControlContentAccessor.SetContent(uc, parentGrid);
        parentGrid.Children.Add(realVisual);

        FrameworkElement dialogElement = LoadDialogResourceXaml();

        parentGrid.Children.Add(dialogElement);
      }
    }
    private static FrameworkElement LoadDialogResourceXaml()
    {
      FrameworkElement element = null;

      using (Stream stream =
        Assembly.GetExecutingAssembly().GetManifestResourceStream("SilverlightApplication16.dialog.xaml"))
      {
        using (StreamReader streamReader = new StreamReader(stream))
        {
          element = XamlReader.Load(streamReader.ReadToEnd()) as FrameworkElement;
          streamReader.Close();
        }
        stream.Close();
      }
      if (element != null)
      {
        Button okButton = element.FindName("buttonOk") as Button;
        Button cancelButton = element.FindName("buttonCancel") as Button;

        if (okButton != null)
        {
          okButton.Click += DismissDialog;
        }
        if (cancelButton != null)
        {
          cancelButton.Click += DismissDialog;
        }
      }
      return (element);
    }
    static void DismissDialog(object sender, EventArgs args)
    {
      UserControl uc = Application.Current.RootVisual as UserControl;

      if (uc != null)
      {
        parentGrid.Children.Clear();
        realVisual.IsHitTestVisible = true;
        UserControlContentAccessor.SetContent(uc, realVisual);
      }
    }
  }

So....

  1. We have a hard-coded UI in a piece of XAML embedded in an assemby ( even the MessageBox message is hard-coded right now :-) )
  2. We have no way of moving the MessageBox around the screen or doing non-modal.
  3. The programming model is a little bit wrong. Generally, MessageBox.Show() doesn't return until the user's clicked one of the buttons. Also, I'm not allowing choice of which buttons are shown/hidden and I'm not returning which button was pressed.

It looks like this;

image

How to move this forward? I figured that one way to do it ( and possibly the best way that I can come up with ) is to make the MessageBox.Show() display a control. Let's say I create some class MessageBoxControl and if you call;

MessageBox.Show("Hello World");

then I'll assume that what you want me to display is the standard MessageBoxControl. However, if you call something like;

MessageBoxControl myControl = new MyDerivedMessageBoxControl();

MessageBox.Show("Hello World", myControl);

then I can show your message box control rather than mine. Why impose a base class on you? It's essentially so that I can be sure that you have some kind of event that fires when someone clicks a button on the control so that I would know when to dismiss the dialog.

I'd want my MessageBoxControl to be a templated control so that you could define one simply as a XAML resource and style it so that it doesn't look like my message box but more like your message box.

This is a bit of a strange one because you wouldn't necessarily need to use the control in your UI but you could just stick a template for it in a resource dictionary so that you can then load that and apply it.

I started to build the control, giving it just 4 simple parts initially - the yes, no, cancel buttons and the root element. I could see having parts such as the part that forms a border, a part that forms the dialog title, a part that might mimic the system menu and so on but I've not done that here. I just kept it simple;

  public enum MessageBoxResult
  {
    Yes,
    No,
    Cancel
  }
  public class MessageBoxResultEventArgs : EventArgs
  {
    public MessageBoxResult Result { get; set; }
    public object AsyncState { get; set; }
  }
  [TemplatePart(Name = RootElement, Type = typeof(Panel))]
  [TemplatePart(Name = YesButtonElement, Type = typeof(Button))]
  [TemplatePart(Name = NoButtonElement, Type = (typeof(Button)))]
  [TemplatePart(Name = CancelButtonElement, Type = (typeof(Button)))]
  public class MessageBoxControl : ContentControl 
  {
    public event EventHandler<MessageBoxResultEventArgs> MessageBoxDismissed;

    public MessageBoxControl()
    {
      DefaultStyleKey = typeof(MessageBoxControl);
    }
    public override void OnApplyTemplate()
    {
      if (yesButton != null)
      {
        yesButton.Click -= OnYesButton;        
      }
      if (noButton != null)
      {
        noButton.Click -= OnNoButton;
      }
      if (cancelButton != null)
      {
        cancelButton.Click -= OnCancelButton;
      }
      rootElement = base.GetTemplateChild(RootElement) as Panel;
      yesButton = base.GetTemplateChild(YesButtonElement) as Button;
      noButton = base.GetTemplateChild(NoButtonElement) as Button;
      cancelButton = base.GetTemplateChild(CancelButtonElement) as Button;

      if (yesButton != null)
      {
        yesButton.Click += OnYesButton;
      }
      if (noButton != null)
      {
        noButton.Click += OnNoButton;
      }
      if (cancelButton != null)
      {
        cancelButton.Click += OnCancelButton;
      }
    }
    void OnYesButton(object sender, EventArgs args)
    {
      FireDismissed(MessageBoxResult.Yes);
    }
    void OnNoButton(object sender, EventArgs args)
    {
      FireDismissed(MessageBoxResult.No);
    }
    void OnCancelButton(object sender, EventArgs args)
    {
      FireDismissed(MessageBoxResult.Cancel);
    }
    void FireDismissed(MessageBoxResult result)
    {
      if (MessageBoxDismissed != null)
      {
        MessageBoxDismissed(this, new MessageBoxResultEventArgs() { Result = result });
      }
    }
    Button yesButton;
    Button noButton;
    Button cancelButton;    
    Panel rootElement;
    public const string RootElement = "RootElement";
    public const string YesButtonElement = "YesButtonElement";
    public const string NoButtonElement = "NoButtonElement";
    public const string CancelButtonElement = "CancelButtonElement";
  }

and this is powered by a generic.xaml that I've embedded in my assembly and that XAML is really just an altered version of the initial XAML that I  had before. That is;

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
  xmlns:local="clr-namespace:SilverlightApplication16">  
  <Style
    TargetType="local:MessageBoxControl">
    <Setter
      Property="Template">
      <Setter.Value>
        <ControlTemplate
          TargetType="local:MessageBoxControl">
          <Grid Width="600" Height="300" x:Name="RootElement">
            <Border
              HorizontalAlignment="Stretch"
              Margin="10,10,10,10"
              VerticalAlignment="Stretch"
              BorderThickness="10,10,10,10"
              CornerRadius="3,3,3,3">
              <Border.BorderBrush>
                <LinearGradientBrush
                  EndPoint="0.5,1"
                  StartPoint="0.5,0">
                  <GradientStop
                    Color="#06000000" />
                  <GradientStop
                    Color="#14000000"
                    Offset="0.628" />
                </LinearGradientBrush>
              </Border.BorderBrush>
              <Grid>
                <Grid.RowDefinitions>
                  <RowDefinition
                    Height="0.124*" />
                  <RowDefinition
                    Height="0.522*" />
                  <RowDefinition
                    Height="0.235*" />
                  <RowDefinition
                    Height="0.119*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                  <ColumnDefinition
                    Width="0.048*" />
                  <ColumnDefinition
                    Width="0.905*" />
                  <ColumnDefinition
                    Width="0.047*" />
                </Grid.ColumnDefinitions>
                <Rectangle
                  Height="Auto"
                  Width="Auto"
                  RadiusX="8.4"
                  RadiusY="8.4"
                  Margin="0,0,0,0"
                  Grid.ColumnSpan="3"
                  Grid.RowSpan="4">
                  <Rectangle.Stroke>
                    <LinearGradientBrush
                      EndPoint="0.5,1"
                      StartPoint="0.5,0">
                      <GradientStop
                        Color="#B3FFFFFF" />
                      <GradientStop
                        Color="#B4FFFFFF"
                        Offset="1" />
                      <GradientStop
                        Color="#FFFBF7F7"
                        Offset="0.54500001668930054" />
                    </LinearGradientBrush>
                  </Rectangle.Stroke>
                  <Rectangle.Fill>
                    <LinearGradientBrush
                      EndPoint="1.1599999666214,1.22000002861023"
                      StartPoint="-0.152999997138977,-0.33899998664856">
                      <GradientStop
                        Color="#FFFFFFFF" />
                      <GradientStop
                        Color="#8CFFFFFF"
                        Offset="1" />
                      <GradientStop
                        Color="#4CFFFFFF"
                        Offset="0.339" />
                      <GradientStop
                        Color="#32FFFFFF"
                        Offset="0.63599997758865356" />
                      <GradientStop
                        Color="#7AFFFFFF"
                        Offset="0.81400001049041748" />
                      <GradientStop
                        Color="#04FFFFFF"
                        Offset="0.93300002813339233" />
                    </LinearGradientBrush>
                  </Rectangle.Fill>
                </Rectangle>
                <Rectangle
                  Height="Auto"
                  HorizontalAlignment="Stretch"
                  Margin="-1,-1,-0.200000002980232,0.200000002980232"
                  VerticalAlignment="Stretch"
                  Width="Auto"
                  Grid.Column="1"
                  Grid.Row="1"
                  Grid.RowSpan="2"
                  Fill="#FFFFFFFF"
                  Stroke="#FF000000" />
                <ContentPresenter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch"
                                  VerticalAlignment="Stretch"/>
                <Rectangle
                  HorizontalAlignment="Stretch"
                  Margin="1,0,1,1"
                  Grid.RowSpan="1"
                  Fill="#FFD3D3D3"
                  Grid.ColumnSpan="1"
                  Grid.Row="2"
                  Grid.Column="1" />
                <StackPanel
                  HorizontalAlignment="Right"
                  Margin="0,0,10,0"
                  Grid.RowSpan="1"
                  Grid.ColumnSpan="1"
                  Grid.Row="2"
                  Grid.Column="1"
                  Orientation="Horizontal">
                  <Button
                    x:Name="YesButtonElement"
                    Content="Yes"
                    Width="124.566"
                    Height="40.844"
                    Margin="5,0,0,0"
                    HorizontalAlignment="Right"/>
                  <Button
                    x:Name="NoButtonElement"
                    Height="40.844"
                    Width="124.566"
                    Content="No"
                    Margin="5,0,0,0"
                    HorizontalAlignment="Right"/>
                  <Button
                    x:Name="CancelButtonElement"
                    Height="40.844"
                    Width="124.566"
                    Content="Cancel"
                    Margin="5,0,0,0"
                    HorizontalAlignment="Right" />
                </StackPanel>
              </Grid>
            </Border>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

With that in place, I can make my MessageBox static class look a bit like;

  public static class MessageBox
  {    
    public static void ShowAsync(object content)
    {
      ShowAsync(content, null);
    }
    public static void ShowAsync(object content,
      EventHandler<MessageBoxResultEventArgs> callback)
    {
      ShowAsync(content, null, callback);
    }
    public static void ShowAsync(object content, object userState,
      EventHandler<MessageBoxResultEventArgs> callback)
    {
      ShowAsync(content, userState, callback, null);
    }
    public static void ShowAsync(object content, object userState,
      EventHandler<MessageBoxResultEventArgs> callback, Style controlTemplate)
    {
      MessageBoxControl control = new MessageBoxControl();
      control.Content = content;

      if (controlTemplate != null)
      {
        control.Style = controlTemplate;
      }
      ShowAsync(control, userState, callback);
    }
    public static void ShowAsync(MessageBoxControl control, object userState,
      EventHandler<MessageBoxResultEventArgs> callback)
    {
      UserControl uc = Application.Current.RootVisual as UserControl;      

      if (uc != null)
      {
        asyncState = userState;
        userCallback = callback;
        realVisual = UserControlContentAccessor.GetContent(uc);
        realVisual.IsHitTestVisible = false;

        parentGrid = new Grid();
        UserControlContentAccessor.SetContent(uc, parentGrid);
        parentGrid.Children.Add(realVisual);
        parentGrid.Children.Add(control);

        control.MessageBoxDismissed += OnDismissed;
      }
    }
    static void OnDismissed(object sender, MessageBoxResultEventArgs e)
    {
      MessageBoxControl control = sender as MessageBoxControl;

      UserControl uc = Application.Current.RootVisual as UserControl;

      if (uc != null)
      {
        parentGrid.Children.Clear();
        realVisual.IsHitTestVisible = true;
        UserControlContentAccessor.SetContent(uc, realVisual);
      }
      if (control != null)
      {
        control.MessageBoxDismissed -= OnDismissed;
      }
      try
      {
        if (userCallback != null)
        {
          userCallback(null, new MessageBoxResultEventArgs()
          {
            Result = e.Result,
            AsyncState = asyncState
          });
        }
      }
      finally
      {
        realVisual = null;
        parentGrid = null;
        asyncState = null;
        userCallback = null;
      }
    }    
    private static UIElement realVisual;
    private static Grid parentGrid;
    private static object asyncState;
    private static EventHandler<MessageBoxResultEventArgs> userCallback;
  }

and then I can start to make use of it with code such as;

      MessageBox.ShowAsync("Simple call, no callback, no state, no style");

or

      MessageBox.ShowAsync(new Ellipse()
      {
        Width = 96,
        Height = 96,
        Fill =
          new SolidColorBrush(Colors.Green)
      });

or

      MessageBox.ShowAsync("As previously but with a callback - hit NO", (s, e) =>
        {
          Debug.Assert(e.Result == MessageBoxResult.No);
        });

or

  MessageBox.ShowAsync("As previously but with state - hit YES", 101, (s, e) =>
        {
          Debug.Assert((e.Result == MessageBoxResult.Yes) && ((int)e.AsyncState == 101));
        });

 

or

      Style myStyle = this.Resources["myStyle"] as Style;

      MessageBox.ShowAsync("Using a different style altogether", null, null,
        myStyle);
 

That last one loads a different style from my resources which ends up looking like this;

image

which was designed more from the point of view of being "different" than being "pretty". The styling for that ends up looking like this;

    <Style
      x:Name="myStyle"
      TargetType="local:MessageBoxControl">
      <Setter
        Property="Template">
        <Setter.Value>
          <ControlTemplate>
            <Grid
              x:Name="RootElement"
              Width="800" Height="600">  
              <Grid.Resources>
                <Style
                  x:Key="ButtonStyle1"
                  TargetType="Button">
                  <Setter
                    Property="IsEnabled"
                    Value="true" />
                  <Setter
                    Property="IsTabStop"
                    Value="true" />
                  <Setter
                    Property="Background"
                    Value="#FF003255" />
                  <Setter
                    Property="Foreground"
                    Value="#FF313131" />
                  <Setter
                    Property="MinWidth"
                    Value="5" />
                  <Setter
                    Property="MinHeight"
                    Value="5" />
                  <Setter
                    Property="Margin"
                    Value="0" />
                  <Setter
                    Property="HorizontalContentAlignment"
                    Value="Center" />
                  <Setter
                    Property="VerticalContentAlignment"
                    Value="Center" />
                  <Setter
                    Property="Cursor"
                    Value="Arrow" />
                  <Setter
                    Property="TextAlignment"
                    Value="Left" />
                  <Setter
                    Property="TextWrapping"
                    Value="NoWrap" />
                  <Setter
                    Property="FontSize"
                    Value="11" />
                  <Setter
                    Property="Template">
                    <Setter.Value>
                      <ControlTemplate
                        TargetType="Button">
                        <Grid>
                          <Grid.Resources>
                            <Color
                              x:Key="LinearBevelLightStartColor">#FCFFFFFF</Color>
                            <Color
                              x:Key="LinearBevelLightEndColor">#F4FFFFFF</Color>
                            <Color
                              x:Key="LinearBevelDarkStartColor">#E0FFFFFF</Color>
                            <Color
                              x:Key="LinearBevelDarkEndColor">#B2FFFFFF</Color>
                            <Color
                              x:Key="MouseOverLinearBevelDarkEndColor">#7FFFFFFF</Color>
                            <Color
                              x:Key="HoverLinearBevelLightStartColor">#FCFFFFFF</Color>
                            <Color
                              x:Key="HoverLinearBevelLightEndColor">#EAFFFFFF</Color>
                            <Color
                              x:Key="HoverLinearBevelDarkStartColor">#D8FFFFFF</Color>
                            <Color
                              x:Key="HoverLinearBevelDarkEndColor">#4CFFFFFF</Color>
                            <Color
                              x:Key="CurvedBevelFillStartColor">#B3FFFFFF</Color>
                            <Color
                              x:Key="CurvedBevelFillEndColor">#3CFFFFFF</Color>
                            <SolidColorBrush
                              x:Key="BorderBrush"
                              Color="#FF000000" />
                            <SolidColorBrush
                              x:Key="AccentBrush"
                              Color="#FFFFFFFF" />
                            <SolidColorBrush
                              x:Key="DisabledBrush"
                              Color="#A5FFFFFF" />
                            <LinearGradientBrush
                              x:Key="FocusedStrokeBrush"
                              EndPoint="0.5,1"
                              StartPoint="0.5,0">
                              <GradientStop
                                Color="#B2FFFFFF"
                                Offset="0" />
                              <GradientStop
                                Color="#51FFFFFF"
                                Offset="1" />
                              <GradientStop
                                Color="#66FFFFFF"
                                Offset="0.325" />
                              <GradientStop
                                Color="#1EFFFFFF"
                                Offset="0.325" />
                            </LinearGradientBrush>
                          </Grid.Resources>
                          <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup
                              x:Name="CommonStates">
                              <vsm:VisualStateGroup.Transitions>
                                <vsm:VisualTransition
                                  Duration="0:0:0.2"
                                  To="MouseOver" />
                                <vsm:VisualTransition
                                  Duration="0:0:0.1"
                                  To="Pressed" />
                              </vsm:VisualStateGroup.Transitions>
                              <vsm:VisualState
                                x:Name="Normal" />
                              <vsm:VisualState
                                x:Name="MouseOver">
                                <Storyboard />
                              </vsm:VisualState>
                              <vsm:VisualState
                                x:Name="Pressed">
                                <Storyboard />
                              </vsm:VisualState>
                              <vsm:VisualState
                                x:Name="Disabled">
                                <Storyboard />
                              </vsm:VisualState>
                            </vsm:VisualStateGroup>
                            <vsm:VisualStateGroup
                              x:Name="FocusStates">
                              <vsm:VisualState
                                x:Name="Focused">
                                <Storyboard />
                              </vsm:VisualState>
                              <vsm:VisualState
                                x:Name="Unfocused">
                                <Storyboard />
                              </vsm:VisualState>
                            </vsm:VisualStateGroup>
                          </vsm:VisualStateManager.VisualStateGroups>
                          <Path
                            HorizontalAlignment="Stretch"
                            Margin="5.80000019073486,4.09999990463257,0.800999999046326,9.66100025177002"
                            VerticalAlignment="Stretch"
                            Fill="#FF06C8B3"
                            Stretch="Fill"
                            Stroke="#FF2400DA"
                            StrokeThickness="2"
                            Data="M8.2999935,10.199994 L6.299994,4.5999937 C6.299994,4.5999937 46.699978,23.399988 65.099976,8.9999876 C83.499969,-5.400013 49.899971,38.601345 58.69997,39.401375 C67.499969,40.201408 23.499971,28.200924 13.099981,37.801304 C2.6999912,47.401684 8.2999935,10.199994 8.2999935,10.199994 z" />
                          <ContentPresenter
                            Margin="4,5,4,4"
                            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                            Padding="{TemplateBinding Padding}"
                            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                            Content="{TemplateBinding Content}"
                            ContentTemplate="{TemplateBinding ContentTemplate}"
                            TextAlignment="{TemplateBinding TextAlignment}"
                            TextDecorations="{TemplateBinding TextDecorations}"
                            TextWrapping="{TemplateBinding TextWrapping}" />
                        </Grid>
                      </ControlTemplate>
                    </Setter.Value>
                  </Setter>
                </Style>

              </Grid.Resources>
                  <Grid.RowDefinitions>
                    <RowDefinition
                      Height="*" />
                  </Grid.RowDefinitions>
                  <Grid.ColumnDefinitions>
                    <ColumnDefinition
                      Width="*" />
                  </Grid.ColumnDefinitions>
                  <Path
                    HorizontalAlignment="Stretch"
                    Margin="53.5,30.5690002441406,35.4770011901855,19.6319999694824"
                    VerticalAlignment="Stretch"
                    Stretch="Fill"
                    StrokeThickness="9"
                    Data="M54,53.599998 C54,53.599998 453.20001,-1.6000016 550.79999,60.799999 C648.39996,123.2 531.59998,208.00238 594.79999,209.60242 C658,211.20244 404.39999,259.20325 334.79999,203.20233 C265.19998,147.20142 67.600006,196.00183 81.200012,172.8015 C94.800018,149.60118 54,53.599998 54,53.599998 z">
                    <Path.Fill>
                      <RadialGradientBrush>
                        <GradientStop
                          Color="#FF6EE7FF" />
                        <GradientStop
                          Color="#FF228A9F"
                          Offset="1" />
                      </RadialGradientBrush>
                    </Path.Fill>
                    <Path.Stroke>
                      <LinearGradientBrush
                        EndPoint="1.02400004863739,0.507000029087067"
                        StartPoint="0.0149999996647239,0.495000004768372">
                        <GradientStop
                          Color="#FF000000" />
                        <GradientStop
                          Color="#FF572AEF"
                          Offset="0.375" />
                        <GradientStop
                          Color="#FF25078C"
                          Offset="0.73900002241134644" />
                      </LinearGradientBrush>
                    </Path.Stroke>
                  </Path>
                  <Button
                    x:Name="YesButtonElement"
                    Height="49.6"
                    HorizontalAlignment="Right"
                    Margin="0,0,103.199996948242,47.2000007629395"
                    VerticalAlignment="Bottom"
                    Width="72"
                    Content="Dismiss"
                    Style="{StaticResource ButtonStyle1}"/>
                  <ContentPresenter
                    Height="70.4"
                    HorizontalAlignment="Stretch"
                    Margin="106.400001525879,66.4000015258789,130.399993896484,113.599998474121"
                    VerticalAlignment="Stretch"
                    FontFamily="Comic Sans MS"
                    FontSize="22"
                    FontStyle="Italic"
                    FontWeight="Bold"/>
                </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

After all that, there's a bunch more stuff that I could do here like;

  1. Adding some visual states to my control so that I can animate between them.
  2. Adding some more visual parts to my control so that (e.g.) there's a title bar, a system menu and so on.
  3. Adding the ability to "move" the dialog around on the screen in response to mouse events on the UI.

but I'll leave it there for now.

As usual, if you want the source to play with then it's here for download.


Filed Under: ,

I'm Missing My Default Parameter Values

I miss default parameter values from C++. In C# if I have some method Foo;

  static void Foo(int intValue, float floatValue, string stringValue)
  {
  }

and there's the possibility that all the parameters can be "optional" then I end up writing something like;

  static void Foo()
  {
    Foo(0, 0.0f, null);
  }
  static void Foo(int intValue)
  {
    Foo(intValue, 0.0f, null);
  }
  static void Foo(int intValue, float floatValue)
  {
    Foo(intValue, floatValue, null);
  }
  static void Foo(int intValue, float floatValue, string stringValue)
  {
  }

which is a lot of code when I really just want to say;

  static void Foo(int intValue = 0, float floatValue = 0.0f, string stringValue = null)
  {
  }

and my C++ compiler would have been happy with that but my C# compiler isn't going to be. Pity as I think that the single function definition is a lot nicer than the multiple function definitions. With object initialisation in C# V3.0 you almost feel like writing;

public class ArgType
{
  public int intValue = 0;
  public float floatValue = 0;
  public string stringValue = null;
}
class Program
{
  static void Main(string[] args)
  {
    Foo(new ArgType() { intValue = 10 });
  }
  static void Foo(ArgType arg)
  {
  }
}

but then two wrongs are never going to make a right :-)


Filed Under: ,

Duplex WCF Services with Silverlight 2

Hmm. Maybe I'm just suffering a post-holiday motivational low ( which I am, in a big way :-) ) but I've been reading this;

Accessing Duplex Services

and I'm struggling to see how the "reward" here is justifying the "effort".

With Silverlight, it's pretty easy for me to call a request-response service on a polled interval. I just;

  1. Write the service exactly as usual.
  2. Configure it exactly as usual.
  3. Generate a proxy exactly as usual.
  4. Call it exactly as usual.
  5. Use a client-side timer to call it again after some interval.

This is really pretty easy stuff and results in a lot less code than the "polling binding" so I'm struggling to see why I'd bother with it. It seems to be;

  1. More code
  2. More complexity
  3. Same (or very similar) functionality

So why would I do it? Anyone? Anyone? :-)


Filed Under: ,

Silverlight 2 Beta 2 - ASMX Services & XmlSerializer

It wasn't clear to me from reading the Silverlight SDK docs whether or not you could call ASMX services from Silverlight 2 Beta 2 if calling those services meant involing the XmlSerializer.

Silverlight is using WCF to call services and WCF usually uses the DataContractSerializer. This is a clever serializer but one of the things that it doesn't do ( which I never understood the rationale for ) is serialize XML attributes. It is as element-centric as a serializer can be.

So, I read in the Silverlight SDK docs;

Using XmlSerializer

  • Silverlight 2 Beta 2 supports XmlSerializer. The equivalent serialization support in WCF is described in XML and SOAP Serialization.
  • In Silverlight 2 Beta 2, generating SOAP messages as described in the preceding document is not supported.

and I didn't understand whether those 2 sentences combined to say "Supported" or "Not Supported". Having read it twice, I think what it means is that the XmlSerializer bits are supported but the bits referenced here are not. That's what I think it means but I can't be 100% sure.

And so I built a quick ASMX service as in;

public class Operand
{
  [System.Xml.Serialization.XmlAttribute]
  public int X { get; set; }

  [System.Xml.Serialization.XmlAttribute]
  public int Y { get; set; }
}

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
  public WebService()
  {
  }
  [WebMethod]
  public int Add(Operand op)
  {
    return (op.X + op.Y);
  }
}

and added a reference to it from my Silverlight 2 Beta 2 project and the proxy generation seemed to work ok and, specifically, I could see that the generated proxy code is using the XmlSerializer;

namespace SilverlightApplication3.ProxyCode {
    
    
    [System.ServiceModel.ServiceContractAttribute()]
    public interface WebServiceSoap {
        
        [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/Add", ReplyAction="*")]
        [System.ServiceModel.XmlSerializerFormatAttribute()]
        System.IAsyncResult BeginAdd(SilverlightApplication3.ProxyCode.Operand op, System.AsyncCallback callback, object asyncState);
        
        int EndAdd(System.IAsyncResult result);
    }

and then I called the service using the proxy and it all seemed to work fine. Note that I had to manually add a reference to System.Xml.Serialization.dll in order to get the client to compile but that's fine.

So..."seems to work".


Filed Under: ,

Silverlight 2 and HTML 5 - Being Online/Offline ( Part 2 )

Following up on this post, it seems that there's more to this online/offline malarky in HTML 5 in that this;

http://www.w3.org/TR/2008/WD-html5-20080122/#appcache 

talks about Application Caches. As far as I can work out, that's to do with providing more of a manifest around what pages/resource need to be cached in order to make the application work well when run entirely from the cache offline.

I was wondering how this would work with Silverlight so I thought I'd try an experiment using that base class that I wrote in the previous post.

Let's say (for argument's sake) that I want to build a simple email client in Silverlight. So, I want a UI with fields such as TO, CC, Subject, Body and I have some webservice that'll actually do the emailing for me.

I want to be able to do this whether I am online or offline. Maybe I want a UI something like this;

image

The idea being that you write a mail and when you click Send we either;

  1. If we're online, call the webservice.
  2. If we're offline, store the mail in IsolatedStorage and next time we go online we send the mail via the webservice.

I built a quick website and added a simple web service;

[ServiceContract]
public interface IMailService
{
  // Assume we know who the mail is from (which we wouldn't :-))
  [OperationContract]  
  void Sen