Silverlight 3 – Simple Control for Online/Offline

Given that Silverlight 3 can detect the online/offline status of the network, I thought I’d have a play with a simple control that displays something when the network is online and something else when the network is offline.

I started with a UserControl but ended up just with a Control using the “parts and states” model that had just one group of states called OnlineOfflineStates with 2 states within it named Online and Offline.

The source for the control is here and it’s just a little code ( might have bugs in it as it’s not been tried very much 🙂 );

namespace MikesControls
{
  [TemplateVisualState(Name="Online", GroupName="OnlineOfflineStates")]
  [TemplateVisualState(Name="Offline", GroupName="OnlineOfflineStates")]
  [TemplatePart(Name="OnlineContentRoot", Type=typeof(Panel))]
  [TemplatePart(Name="OfflineContentRoot", Type=typeof(Panel))]
  public class OnlineOfflineControl : Control
  {
    public OnlineOfflineControl()
    {
      this.DefaultStyleKey = typeof(OnlineOfflineControl);
      this.Loaded += OnLoaded;
    }    
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      DetermineNetworkStatus();
      NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
    }
    void DetermineNetworkStatus()
    {
      IsOnline = NetworkInterface.GetIsNetworkAvailable();
    }
    void OnNetworkAddressChanged(object sender, EventArgs e)
    {
      DetermineNetworkStatus();
      UpdateOnlineOfflineUI();
    }
    public override void OnApplyTemplate()
    {
      onlineUI = base.GetTemplateChild("OnlineContentRoot") as Panel;
      offlineUI = base.GetTemplateChild("OfflineContentRoot") as Panel;
      base.OnApplyTemplate();
      UpdateOnlineOfflineUI();
    }
    public static DependencyProperty IsOnlineProperty =
      DependencyProperty.Register("IsOnline", typeof(bool), typeof(OnlineOfflineControl), null);        

    public bool IsOnline
    {
      get
      {
        return ((bool)base.GetValue(IsOnlineProperty));
      }
      internal set
      {
        base.SetValue(IsOnlineProperty, value);
      }
    }
    public static DependencyProperty OnlineContentProperty =
      DependencyProperty.Register("OnlineContent", typeof(object), typeof(OnlineOfflineControl), null);

    public object OnlineContent
    {
      get
      {
        return (base.GetValue(OnlineContentProperty));
      }
      set
      {
        base.SetValue(OnlineContentProperty, value);
      }
    }
    public static DependencyProperty OfflineContentProperty =
      DependencyProperty.Register("OfflineContent", typeof(object), typeof(OnlineOfflineControl), null);

    public object OfflineContent
    {
      get
      {
        return (base.GetValue(OfflineContentProperty));
      }
      set
      {
        base.SetValue(OfflineContentProperty, value);
      }
    }
    void UpdateOnlineOfflineUI()
    {
      string state = IsOnline ? "Online" : "Offline";
      VisualStateManager.GoToState(this, state, true);
    }
    Panel onlineUI;
    Panel offlineUI;
  }
}

and that goes hand in hand with a lump of XAML in the Themes/generic.xaml file;

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
    xmlns:local="clr-namespace:MikesControls"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Style
        TargetType="local:OnlineOfflineControl">
        <Setter
            Property="Template">
            <Setter.Value>
                <ControlTemplate
                    TargetType="local:OnlineOfflineControl">
                    <Grid
                        Background="{TemplateBinding Background}">
                        <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup
                                x:Name="OnlineOfflineStates">
                                <vsm:VisualState
                                    x:Name="Online">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="OfflineContentRoot"
                                            Storyboard.TargetProperty="Opacity"
                                            To="0.0" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="OnlineContentRoot"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1.0" />
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState
                                    x:Name="Offline">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="OfflineContentRoot"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1.0" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="OnlineContentRoot"
                                            Storyboard.TargetProperty="Opacity"
                                            To="0.0" />
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                        </vsm:VisualStateManager.VisualStateGroups>
                        <Grid
                            x:Name="OnlineContentRoot">
                            <ContentPresenter
                                Content="{TemplateBinding OnlineContent}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                        </Grid>
                        <Grid
                            x:Name="OfflineContentRoot">
                            <ContentPresenter
                                Content="{TemplateBinding OfflineContent}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

and so the control ends up being;

  1. A template part called OnlineContentRoot which will, by default, show a ContentPresenter displaying the contents of a property called OnlineContent
  2. A template part called OfflineContentRoot which will, by default, show a ContentPresenter displaying the contents of a property called OfflineContent
  3. A couple of states – in the Online state we will display OnlineContentRoot and hide OfflineContentRoot and vice versa in the Offline state

that then means that I can use this control in a simple way by just setting the OnlineContent and OfflineContent as in;

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="clr-namespace:MikesControls;assembly=MikesControls" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    x:Class="OnlineOffline.MainPage">
    <StackPanel>
        <mc:OnlineOfflineControl
            x:Name="onlineOffline">
            <mc:OnlineOfflineControl.OnlineContent>
                <TextBlock
                    FontSize="36"
                    Text="Online" />
            </mc:OnlineOfflineControl.OnlineContent>
            <mc:OnlineOfflineControl.OfflineContent>
                <TextBlock
                    FontSize="36"
                    Text="Offline" />
            </mc:OnlineOfflineControl.OfflineContent>
        </mc:OnlineOfflineControl>
        <Button
            Content="Click Me When Online"
            IsEnabled="{Binding ElementName=onlineOffline, Path=IsOnline}" />
    </StackPanel>
</UserControl>

or perhaps;

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="clr-namespace:MikesControls;assembly=MikesControls" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    x:Class="OnlineOffline.MainPage">
    <StackPanel>
        <mc:OnlineOfflineControl
            Width="192"
            Height="192"
            x:Name="onlineOffline">
            <mc:OnlineOfflineControl.OnlineContent>
                <Image
                    Source="/NetworkUp.png" />
            </mc:OnlineOfflineControl.OnlineContent>
            <mc:OnlineOfflineControl.OfflineContent>
                <Image
                    Source="/NetworkDown.png" />
            </mc:OnlineOfflineControl.OfflineContent>
        </mc:OnlineOfflineControl>
        <Button
            HorizontalAlignment="Center"
            Content="Click Me When Online"
            IsEnabled="{Binding ElementName=onlineOffline, Path=IsOnline}" />
    </StackPanel>
</UserControl>

which displays with these 2 icons (taken from here) when online/offline;

image image

or the whole control can be re-templated by just dropping into Expression Blend and providing new values for the 2 parts OnlineContentRoot and OfflineContentRoot and then controlling the transitions between the states Online and Offline.