Robert Westerlund

A developer's blog on code, technology and tools in the Web, .NET and other development areas.

NAVIGATION - SEARCH

Custom scroll buttons for the ScrollViewer control in Silverlight

Earlier today I had a discussion with a co-worker who needed to replace the default template of the ScrollViewer control, in order to replace the ScrollBar with custom buttons. He had found some example of doing this in WPF, but the same sample did not work in Silverlight (the samples relied on routed command which aren’t available in Silverlight) so I decided to write up a sample of my own.

Disclaimer: The sample is very rudimentary and may not be the best way to achieve this (also the placement of the commands might be questionable). Use the sample at your own risk.

To begin with, we need a ScrollViewer with some content.

<Grid x:Name="LayoutRoot" Background="White" Height="100" Width="120">
    <ScrollViewer>
        <StackPanel>
            <TextBlock Text="Text1"/>
            <TextBlock Text="Text2"/>
            <TextBlock Text="Text3"/>
            <TextBlock Text="Text4"/>
            <TextBlock Text="Text5"/>
            <TextBlock Text="Text6"/>
            <TextBlock Text="Text7"/>
            <TextBlock Text="Text8"/>
            <TextBlock Text="Text9"/>
        </StackPanel>
    </ScrollViewer>
</Grid>

Additionally, we will be using a stripped down version of the RelayCommand. I use a stripped down version since I really don’t need any more functionality for this sample.

public class RelayCommand<T>:ICommand
{
    private Action<T> _action;

    public RelayCommand(Action<T> action)
    {
        this._action = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (!(parameter is T))
            throw new ArgumentException("The parameter must be of type T", "parameter");
        _action((T)parameter);
    }
}

The default look of the ScrollViewer control, using the above Xaml looks similar to this:

The default look of the ScrollViewer control, using the Content we supplied

And our goal is to make it look like this:

The modified look of the ScrollViewer control, using buttons instead of the ScrollBar, which we have as our aim

Quite an improvement, right?

I admit that it might not be prettiest design for my custom scroll buttons. To my defence I will say that beauty is not the aim of this post and… well, at least it works.

To get the ScrollViewer to look like what we want, we start by changing the declaration of the ScrollViewer to start using a custom template (if you do this in production code, you might want to define it as a style and set the Template property to be your ContentTemplate, especially since you will probably want to set other properties as well, but in order to keep the code a little shorter here, we’ll just set the template directly) which we will define later. The new declaration of the ScrollViewer should be:

<ScrollViewer Template="{StaticResource MyScrollViewer}">
...

The next step is to create the actual Template that we have just referenced. In our template, we’ll create two RepeatButtons (using RepeatButtons instead of Buttons allows the user to keep the button pressed to keep scrolling until released), to serve as up and down buttons, and between them we’ll place the ScrollContentPresenter (which will display the content of the ScrollViewer control, i.e. the TextBlocks).

<ControlTemplate x:Key="MyScrollViewer" TargetType="ScrollViewer">
    <StackPanel Width="100">
        <RepeatButton Content="Up" Command="{Binding ScrollUpCommand, ElementName=_this}"
		CommandParameter="{Binding ElementName=ScrollContentPresenter}"/>
        <ScrollContentPresenter Height="50" x:Name="ScrollContentPresenter"/>
        <RepeatButton Content="Down" Command="{Binding ScrollDownCommand, ElementName=_this}"
		CommandParameter="{Binding ElementName=ScrollContentPresenter}"/>
    </StackPanel>
</ControlTemplate>

So, what is the ScrollUpCommand used in the code above? The ScrollUpCommand, as well as the ScrollDownCommand, is defined in the code behind of the control, using the following code:

public ICommand ScrollUpCommand
{
	get
	{
		return new RelayCommand<ScrollContentPresenter>(scroll => scroll.LineUp());
	}
}

public ICommand ScrollDownCommand
{
	get
	{
		return new RelayCommand<ScrollContentPresenter>(scroll => scroll.LineDown());
	}
}

And there we have it, our nice little ScrollViewer without the default ScrollBar. The secret sauce lies in the CommandParameter, sending the ScrollContentPresenter which we want to scroll as a parameter to the command when it executes. Have fun with the code! Here is the full code for the sample:

MainPage.xaml:

<UserControl x:Class="SilverlightApplication11.MainPage"
    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" x:Name="_this">
    <UserControl.Resources>
        <ControlTemplate x:Key="MyScrollViewer" TargetType="ScrollViewer">
            <StackPanel Width="100">
                <RepeatButton Content="Up" Command="{Binding ScrollUpCommand, ElementName=_this}" 
			CommandParameter="{Binding ElementName=ScrollContentPresenter}"/>
                <ScrollContentPresenter Height="50" x:Name="ScrollContentPresenter"/>
                <RepeatButton Content="Down" Command="{Binding ScrollDownCommand, ElementName=_this}" 
			CommandParameter="{Binding ElementName=ScrollContentPresenter}"/>
            </StackPanel>
        </ControlTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White" Height="100" Width="120">
        <ScrollViewer Template="{StaticResource MyScrollViewer}">
            <StackPanel>
                <TextBlock Text="Text1"/>
                <TextBlock Text="Text2"/>
                <TextBlock Text="Text3"/>
                <TextBlock Text="Text4"/>
                <TextBlock Text="Text5"/>
                <TextBlock Text="Text6"/>
                <TextBlock Text="Text7"/>
                <TextBlock Text="Text8"/>
                <TextBlock Text="Text9"/>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</UserControl>

MainPage.xaml.cs:

using System.Windows.Controls;
using System.Windows.Input;

namespace SilverlightApplication11
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        public ICommand ScrollUpCommand
        {
            get
            {
                return new RelayCommand<ScrollContentPresenter>(scroll => scroll.LineUp());
            }
        }

        public ICommand ScrollDownCommand
        {
            get
            {
                return new RelayCommand<ScrollContentPresenter>(scroll => scroll.LineDown());
            }
        }
    }
}

RelayCommand.cs:

using System;
using System.Windows.Input;

namespace SilverlightApplication11
{
    public class RelayCommand<T>:ICommand
    {
        private Action<T> _action;

        public RelayCommand(Action<T> action)
        {
            this._action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            if (!(parameter is T))
                throw new ArgumentException("The parameter must be of type T", "parameter");
            _action((T)parameter);
        }
    }
}

Creating a Custom Part Catalog for the Managed Extensibility Framework

I'm very fond of the Managed Extensibility Framework (MEF). MEF makes it very easy to create extendable and composable applications. At TechEd in Los Angeles this year Jason Olson held a presentation on opening up an application for extendability with MEF. During his session he presented a sample of a network status aware catalog, which seemed very easy to create and also felt as a very good example of a custom part catalog. Also, I haven't been able to find a good example of how to create a custom part catalog which supports recomposable imports (which is embarrasingly easy, once you know which interface to implement).Unfortunately, I haven't been able to find his code anywhere on the internet and thus decided to create a catalog inspired by his.

UPDATE:When I started writing this post (it took a while to get the focus to complete it), I couldn't find information on creating custom catalogs, but when looking again I found that an example has been created on the MEF codeplex page.

Disclaimer: All code in this post is Demo code and is supposed to be taken as such. Feel free to use the code in any manner, but always be sure you understand what copied code does and how it is done. I do not make any guarantees that code in my posts are production ready.

Introducing the Network Status Aware Catalog

The network status aware catalog is a filter catalog which makes its' decisions based on whether or not a network connection could be found. The MSDN article on the NetworkInterface.GetIsNetworkAvailable()-method (the method that the catalog will use to determine whether or not it has a network) defines a network availablility as:

"A network connection is considered to be available if any network interface is marked 'up' and is not a loopback or tunnel interface."

This does not guarantee a connection to the internet, but it is enough for our demo code.

Giving the catalog the functionality to support recomposition allows imports to state that composition containers are allowed to exchange imported parts when some state change occurs in the catalog (or the catalog for some other reason decides that it is time to perform recomposition).

The Network Status enum

Since I really hate magic strings, I will be using a simple enum to define network statuses.

public enum NetworkStatus
{
  Online,
  Offline
}

A simple enum, but beyond doubt complex enough.

Creating a filtering Part Catalog

It is very easy to create a filtering for MEF is very easy. Just inherit from the ComposablePartCatalog class and implement the abstract property called Parts. Since we want a filtering part catalog, we will also add a constructor which receives a ComposablePartCatalog as a parameter.

public class NetworkStatusAwareCatalog : ComposablePartCatalog
{
    private ComposablePartCatalog _composablePartCatalogToFilter;
    public NetworkStatusAwareCatalog(ComposablePartCatalog composablePartCatalogToFilter)
    {
        this._composablePartCatalogToFilter = composablePartCatalogToFilter;
    }

    public override IQueryable Parts
    {
        get { return _composablePartCatalogToFilter.Parts; }  
    }
}


Since the Parts property is an IQueryable, it is very easy to use link to add the filtering we want (we will be implementing the missing methods in a short while).

public override IQueryable Parts
{
    get 
    { 
        return _catalogToFilter
            .Parts
            .Where(p => !IsNetworkStatusAware(p)
                || DoesCurrentNetworkStatusMatchPartNetworkStatus(p)
            ); 
    }
}


This looks very nice so far. However, as we can see we need some way to know if a part is “network status aware” or not (that is, whether a part should only be used when the network status is either online or offline). This can be used by adding PartMetaData on Exported types. In order to understand how it would work, we here create two controls to export, one which should only be used when the network is in online status and one which should only be used in offline status (Again, I really dislike the magic string here, but as Demo code the strings here actually make the code in the post shorter and we will thus use it anyway).

[Export("NetworkStatusAwareControl", typeof(UIElement))]
[PartMetadata("NetworkStatus", NetworkStatus.Online)]
public partial class ControlWhichShouldOnlyBeUsedWhenNetworkIsOnline : UserControl
{
    //The content of this UserControl is not interesting in this post and has thus been omitted.
}

[Export("NetworkStatusAwareControl", typeof(UIElement))]
[PartMetadata("NetworkStatus", NetworkStatus.Offline)]
public partial class ControlWhichShouldOnlyBeUsedWhenNetworkIsOffline : UserControl
{
    //The content of this UserControl is not interesting in this post and has thus been omitted.
}


Accessing this part metadata is very easy to do in the ComposablePartCatalog and thus implementing the two methods IsNetworkStatusAware and DoesCurrentNetworkStatusMatchPartNetworkStatus will now be very easy (we also add a private variable called _currentNetworkStatus which, of course, should contain the current network status).

private NetworkStatus _currentNetworkStatus = NetworkStatus.Offline;
private bool IsNetworkStatusAware(ComposablePartDefinition part)
{
    return part.Metadata.ContainsKey(MetadataKeys.NetworkStatus);
}

private bool DoesCurrentNetworkStatusMatchPartNetworkStatus(ComposablePartDefinition part)
{
    return part.Metadata["NetworkStatus"] is NetworkStatus
        && CurrentNetworkStatusMatches((NetworkStatus)part.Metadata["NetworkStatus"]);
}

private bool CurrentNetworkStatusMatches(NetworkStatus status)
{
    if (Enum.IsDefined(typeof(NetworkStatus), status))
        return status == _currentNetworkStatus;
    else
        return false;
}

So what do we have now? We have created our custom ComposablePartCatalog and it filters the available Parts (if they are “network status aware”) based on the “current” network status.

What about recomposition?

We have only two things left to support in our simple network status aware catalog. It is knowing the current network status and we also need to support recomposition. One step at a time. Lets start with supporting recomposition. As I mentioned previously, supporting recomposition is very easy when you know which interface to implement, since the interface only defines one single event; the Changed event.

We start by adding the implementation of the interface to the class definition as well as implementing the event and adding a protected OnChanged method.

public class NetworkStatusAwareCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged
{
    //The rest of the class is the same as it was previously and has been emitted.
    public event EventHandler Changed;
    protected void OnChanged()
    {
        var changedEvent = Changed;
        if (changedEvent != null)
            changedEvent(this, new ComposablePartCatalogChangedEventArgs(this.Parts));
    }
}
Only one more thing to do now; updating our "current network status" when the network status availability changes and calling the OnChanged method when things change. To our help comes the NetworkInterface class and the NetworkChange class. The NetworkInterface class has the method GetIsNetworkAvailable and the NetworkChange class has the event NetworkAvailabilityChanged (in a Silverlight test application I used the NetworkAddressChanged event instead, which seemed to work just fine for this purpose).
private void CheckNetworkStatus()
{
    var previousNetworkStatus = _currentNetworkStatus;
    if (NetworkInterface.GetIsNetworkAvailable())
        _currentNetworkStatus = NetworkStatus.Online;
    else
        _currentNetworkStatus = NetworkStatus.Offline;
    if (_networkStatus != previousNetworkStatus)
        OnChanged();
}

With this method ready, we just need to call it once initially and then every time the network availability changes (or address changes). The constructor would be a perfect place to add these calls.

public NetworkStatusAwareCatalog(ComposablePartCatalog composablePartCatalogToFilter)
{
    this._composablePartCatalogToFilter = composablePartCatalogToFilter;
    NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
    CheckNetworkStatus();
}

private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
    CheckNetworkStatus();
}

Using the network status aware catalog

Have we done all this work for nothing since we still haven’t used this catalog at all? No, of course not. Using it is extremely simple.

In my test application, which was done in Silverlight, I had ContentControl bound to a property of a custom UserControl. The property of the UserControl is a dependency property (in order to support binding, not necessary for MEF).

[Import("NetworkStatusAwareControl", typeof(UIElement), AllowRecomposition = true)]
public UIElement ChildControls
{
    get { return (UIElement)GetValue(ChildControlsProperty); }
    set { SetValue(ChildControlsProperty, value); }
}

Observe that we have specified in the attribute to the property to import using the string “NetworkStatusAwareControl” with the type UIElement (the string and type matches those of the Export) and that this is where we say AllowRecomposition = true. Creating the UserControl and allowing the container to do its composition magic is now very easy (since my application was a Silverlight application, this code is taken from the application startup event handler of the App class).

var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(new NetworkStatusAwareCatalog(catalog));
this.RootVisual = container.GetExportedObject(ExportConstants.RootVisual);