Silverlight video player. YouTube style.

In the previous article I explained how to implement a simple video player with some controls. It works well, but it has a generic style. In this article I’ll explain how to restyle the video player so that it looks the same as YouTube player.

The final result is in the picture below, and at this link you can see how it looked before.
Silverlight_youtube_style

Read more of this post

Advertisements

Silverlight MediaElement tutorial. Creating a video and audio player.

Video in Silverlight is played by using the built-in MediaElement class. You might have seen some video frameworks like Silverlight Media Framework, Expression Encoder Player or Open Video Player, they all are based on this class. Though these frameworks look quite complex, in reality they are just wrappers around the built-in MediaElement or SmoothStreamingMediaElement classes and all that they do is adding design, control buttons and some extra functionality like advertisement and diagnostics. So you can build such video player by yourself.

The simplest media player

Here is the simplest code which will work:

<MediaElement 
    Source="http://ecn.channel9.msdn.com/o9/content/smf/progressivecontent/wildlife.wmv" />

It will start playing video as soon as the page is loaded and the video fills the entire page uniformly (keeping its aspect ratio).
Simplest_MediaElement

If it doesn’t work, make sure that the address of your page looks like `http://…` and not `file://…`. You can fix the url in this way by adding a web application project to your solution and running it.

Now I will explain how to improve the code above so that it looks more like a video player.
Read more of this post

Silverlight sparkline chart

According to Wikipedia

A sparkline is a very small line chart, typically drawn without axes or coordinates.

Though this chart is not included by default to the Silverlight Toolkit library, it is very easy to implement such chart by styling the built-in Line chart. Here is an example which I’ve implemented:

silverlight_sparkline_sample

At first I’ve changed the default control template of the Chart control, I’ve removed all paddings, the chart title, and reduced the minimum height and width of the chart. Also I’ve changed the DataPoint template (made them invisible) and the Polyline template (reduced its thickness). Here is the XAML code:

<UserControl.Resources>
    <Style x:Key="ChartWithoutPaddings" TargetType="chart:Chart">
        <Setter Property="Padding" Value="0" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="ChartAreaStyle">
            <Setter.Value>
                <Style TargetType="Panel">
                    <Setter Property="MinWidth" Value="100" />
                    <Setter Property="MinHeight" Value="20" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="PlotAreaStyle">
            <Setter.Value>
                <Style TargetType="Grid">
                    <Setter Property="Background" Value="Transparent" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="chart:Chart">
                    <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <chartingprimitives:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
                            <Grid Canvas.ZIndex="-1" Style="{TemplateBinding PlotAreaStyle}" />
                        </chartingprimitives:EdgePanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="EmptyDataPoint" TargetType="Control">
        <Setter Property="Background" Value="Black" />
        <Setter Property="Template" Value="{x:Null}" />
    </Style>

    <Style x:Key="OnePixelLine" TargetType="Polyline">
        <Setter Property="StrokeThickness" Value="1" />
    </Style>
</UserControl.Resources>

This code is almost all that you need to create a sparkline chart. All that is left is to remove axes. It wasn’t a trivial thing, so I used some kind of a hack: I set their width (for Y axis) and height (for X axis) to zero.

<chart:Chart Style="{StaticResource ChartWithoutPaddings}">
    <chart:LineSeries ItemsSource="{Binding FirstIndexItems}" IndependentValuePath="Number" DependentValuePath="Value" 
                        DataPointStyle="{StaticResource EmptyDataPoint}" 
                        PolylineStyle="{StaticResource OnePixelLine}"  />
    <chart:Chart.Axes>
        <chart:LinearAxis Orientation="X" Height="0" Opacity="0" />
        <chart:LinearAxis Orientation="Y" Width="0" Opacity="0" />
    </chart:Chart.Axes>
</chart:Chart>

Links
Source code: SparklineChartSample.zip

Silverlight and WP7 chart with data point labels

Recently I found the question on stackoverflow in which one user asked how to add labels to data points on WPF/Silverlight/Windows Phone Toolkit charts. Whereas almost every javascript chart can do this, this functionality is absent in Silverlight charts and none of developers cares.

So I wrote a simple class which extended the LinearSeries class and now line charts can be displayed like this:

silverlight_chart_datapointlabels

wp7_chart_datapointlabels

I added labels only for line charts, but if someone needs them for column charts, I can try to implement it as well.
In the current implementation I created the class which inherits the LineSeries class. It has the following properties:
DisplayLabels – You should set it to true explicitly so that labels are displayed. It is false by default.
LabelBindingPath – Optional. You can specify a custom property of your model which will be displayed instead of the value from the Y-axis.
LabelStyle – Optional. You can change foreground, font weight, font size and other properties of labels.

The example of usage:

<chart:Chart Width="600" Height="300">
    <local:ExtendedLineSeries
        ItemsSource="{Binding Items}"
        DependentValuePath="ItemValue"
        IndependentValuePath="Title" 
        DisplayLabels="True"
        LabelBindingPath="ItemValue" />
</chart:Chart>

The complete source code of the class:

public class ExtendedLineSeries : LineSeries
{
    private Canvas _labelsCanvas;
    private Dictionary<DataPoint, TextBlock> _currentLabels = new Dictionary<DataPoint, TextBlock>();

    /// <summary>
    /// Gets or sets a value indicating whether labels should be displayed. 
    /// </summary>
    public bool DisplayLabels { get; set; }

    /// <summary>
    /// Gets or sets the binding path of the label.
    /// </summary>
    public string LabelBindingPath { get; set; }

    /// <summary>
    /// Gets or sets the style of each label.
    /// </summary>
    public Style LabelStyle { get; set; }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // get a canvas to which the labels will be added
        this._labelsCanvas = (Canvas)this.GetTemplateChild("PlotArea");
        // clear the clip property so that labels are visible even if they exceed the bounds of the chart
        this.Clip = null;
    }

    protected override void UpdateDataPoint(DataPoint dataPoint)
    {
        base.UpdateDataPoint(dataPoint);

        // after the data point is created and added to the chart, we can add a label near it
        if (this.DisplayLabels && dataPoint.Visibility == System.Windows.Visibility.Visible)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() => this.CreateLabel(dataPoint));
        }
    }

    private void CreateLabel(DataPoint dataPoint)
    {
        // this method is also called with the SizeChanged event, so I create the label only one time
        TextBlock label;
        if (this._currentLabels.ContainsKey(dataPoint))
        {
            label = this._currentLabels[dataPoint];
        }
        else
        {
            label = new TextBlock();
            this._labelsCanvas.Children.Add(label);
            this._currentLabels.Add(dataPoint, label);

            label.Style = this.LabelStyle;

            // bind the label text to the specified path, or to dataPoint.DependantValue by default
            Binding binding = this.LabelBindingPath != null
                        ? new Binding(this.LabelBindingPath) { Source = dataPoint.DataContext }
                        : new Binding("DependentValue") { Source = dataPoint };
            BindingOperations.SetBinding(label, TextBlock.TextProperty, binding);
        }

        // calculate a position of the label
        double coordinateY = Canvas.GetTop(dataPoint) - label.ActualHeight; // position the label above the data point
        double coordinateX = Canvas.GetLeft(dataPoint) + dataPoint.ActualHeight / 2 - label.ActualWidth / 2; // center horizontally
        Canvas.SetTop(label, coordinateY);
        Canvas.SetLeft(label, coordinateX);
    }
}

In this code I made an override of the UpdateDataPoint method and each time a DataPoint is updated I update its label.
The code work with Silverlight as well as with Windows Phone 7. For WP7 I used the recompiled library which I published in one of my previous posts.

Source code of the sample application: ChartPointLabelsSample.zip

Highlight found text in ListBox for Silverlight and WP7

You probably saw an implementation of search where all matches of the entered query were highlighted in the text or list, or table. Here are 2 screenshots which illustrate the described functionality:

It is very easy to implement in Javascript, just 4 lines:

var html = element.html();
html = html.replace(new RegExp("<span class=\"highlighted\">(.*?)<\/span>", "i"), "$1"); // clear the previous highlight
html = html.replace(new RegExp("(" + query + ")", "i"), "<span class=\"highlighted\">$1</span>"); // highlight the current search query
element.html(html);

But it is slightly more difficult in Silverlight. However the approach is the same:
1. Get the text value.
2. Replace some places in the text by a span with some user-defined style.
3. Parse the result text and display it.

I have created a new project by using the Windows Phone Databound Application template in Visual Studio, so that I don’t need to create base mark-up and add default data, Visual Studio creates everything by itself.

At first, we need to write methods which replace values of ListBox by formatted values. The corresponding javascript code is `html.replace(new Regexp(…))`. The C# code looks so:

public void Search()
{
    this.ClearHighlights();

    if(string.IsNullOrEmpty(SearchQuery))
    {
        return;
    }

    this.AddHighlights();
}

private void ClearHighlights()
{
    var highlightRegex = new Regex("<Run Foreground='Yellow'>(.*?)</Run>");
    foreach (var item in this.Items)
    {
        item.LineOne = highlightRegex.Replace(item.LineOne, "$1");
        item.LineTwo = highlightRegex.Replace(item.LineTwo, "$1");
    }
}

private void AddHighlights()
{
    var searchRegex = new Regex(string.Format("({0})", this.SearchQuery), RegexOptions.IgnoreCase);
    foreach (var item in this.Items)
    {
        item.LineOne = searchRegex.Replace(item.LineOne, "<Run Foreground='Yellow'>$1</Run>");
        item.LineTwo = searchRegex.Replace(item.LineTwo, "<Run Foreground='Yellow'>$1</Run>");
    }
}

The `LineOne` and `LineTwo` properties contain some strings which are displayed in each item of the ListBox.

Finally, we need to write a binding which correctly displays a text with the `Run` tags. The corresponding javascript code is `elem.html(html)`. I used attached property for this task:

public static class TextBlockProperties
{
    public static string GetStyledText(DependencyObject obj)
    {
        return (string)obj.GetValue(StyledTextProperty);
    }

    public static void SetStyledText(DependencyObject obj, string value)
    {
        obj.SetValue(StyledTextProperty, value);
    }

    public static readonly DependencyProperty StyledTextProperty =
        DependencyProperty.RegisterAttached("StyledText", typeof(string), typeof(TextBlock), new PropertyMetadata(null, StyledText_Changed));


    private static void StyledText_Changed(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        var tb = (TextBlock) d;
        var text = (string) args.NewValue;

        if(string.IsNullOrEmpty(text) || !Regex.IsMatch(text, "<Run.*?>.*?</Run>"))
        {
            tb.Text = text;
            return;
        }

        var formattedTextBlockXaml = "<TextBlock xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" + text + "</TextBlock>";
        var formattedTextBlock = (TextBlock) XamlReader.Load(formattedTextBlockXaml);

        // detach parsed inlines from the view tree
        var inlines = formattedTextBlock.Inlines.ToList();
        formattedTextBlock.Inlines.Clear();

        // add inlines to the specified text block
        tb.Inlines.Clear();
        foreach (var inline in inlines)
        {
            tb.Inlines.Add(inline);
        }
    }
}

So now all that you need is to use the StyledText property instead of Text:

<TextBlock ext:TextBlockProperties.StyledText="{Binding LineOne}" />

In my example I used the yellow color for highlighting, but you can change any property of the `Run` class, like FontWeight (Bold), FontStyle (Italic) and others, the list of properties you can find here: Run Class.

The sample application which I used in order to make the 2 screenshots above you can download here: Wp7ListSearchSample.zip

Updated BindableTabControl post

It is an update to this post.

In that post I implemented the extended TabControl which supported data binding, however it had one inconvenience that it didn’t allow to access and customize its TabItems. Personally me, I have never used TabItems, but it happened that one guy needed to change the Visibility property of specific items, here is his comment. So he added some code to my class and send it to me.

If you use the control from the original post, XAML code will look like this:

<local:ExtendedTabControl 
    ItemsSource="{Binding}" 
    ItemTemplate="{StaticResource ExampleHeaderTemplate}" 
    ContentTemplate="{StaticResource ExampleContentTemplate}" />

Now you can use the third property TabItemTemplate and write your code so:

<local:ExtendedTabControl ItemsSource="{Binding}">
	<local:ExtendedTabControl.TabItemTemplate>
		<DataTemplate>
			<controls:TabItem Visibility="{Binding Visibility}" HeaderTemplate="{StaticResource ExampleHeaderTemplate}" ContentTemplate="{StaticResource ExampleContentTemplate}" />
		</DataTemplate>
	</local:ExtendedTabControl.TabItemTemplate>
</local:ExtendedTabControl>

The first code is enough in most cases, but if you want to use some properties of the TabItem class like Visibility, Style, Template, it is preferable to use the second code.

The source code of the updated control is available here.

Working with JSON web services in Silverlight and Windows Phone 7

In this post I’ll explain how to retrieve and display data from JSON web services.

As an example I’ll use the REST service from geonames.org.

JSON response of the example looks so:

At first, you should create C# classes (models) which can be mapped to JSON entities.
The first curly braсe indicates that it is an object, and it has 1 property `geonames`. After this property you can see the square bracket indicating an array. This array consists of many objects, and each object has 12 properties.
So we should create a model for the root object, which looks so:

// { geonames: [{}, {}, {}] }
[DataContract]
public class CitiesList
{
	[DataMember(Name = "geonames")]
	public List<City> Cities { get; set; }
}

It is important to add DataContract and DataMember attributes to the class and all its properties. Also you should specify the name of the corresponding JSON property (geonames) inside the DataMember attribute.

Then we should create the model for inner objects (cities). As you already saw, each JSON object has 12 properties, but we don’t need all of them. So we can specify only those proeprties which we need, and .Net deserializer will ignore all other properties.

// { name: "", countrycode: "", population: -1 }
[DataContract]
public class City
{
	[DataMember(Name = "name")]
	public string Name { get; set; }

	[DataMember(Name = "countrycode")]
	public string CountryCode { get; set; }

	[DataMember(Name = "population")]
	public int Population { get; set; }
}

So now all that you need is to create an instance of the HttpWebRequest class, get the response and deserialize it by using the DataContractJsonSerializer class. The deserializer requires the System.Servicemodel.Web and System.Runtime.Serialization references.

You must use the two following methods to perform loading and parsing of JSON.

private void BeginDownloadCitiesRequest()
{
	// create the http request
	HttpWebRequest httpWebRequest = WebRequest.CreateHttp("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=en&username=vortexwolf");
	httpWebRequest.Method = "GET";
	httpWebRequest.Accept = "application/json";

	// get the response asynchronously
	httpWebRequest.BeginGetResponse(OnGetResponseCompleted, httpWebRequest);
}

private void OnGetResponseCompleted(IAsyncResult ar)
{
	var httpWebRequest = (HttpWebRequest)ar.AsyncState;

	// get the response
	var response = httpWebRequest.EndGetResponse(ar);

	// deserialize json
	var jsonSerializer = new DataContractJsonSerializer(typeof(CitiesList));
	var responseObject = (CitiesList)jsonSerializer.ReadObject(response.GetResponseStream());

	// display on the view
	Deployment.Current.Dispatcher.BeginInvoke(() => OnCitiesDownloaded(responseObject));
}

The code of creating a http request and getting a response will often repeat in different places of the application, so it is preferrable to move this code to a separate class.
I did it so:

public class HttpGetTask<T>
{
    public HttpGetTask(string url, Action<T> onPostExecute)
    {
        this.Url = url;
        this.OnPostExecute = onPostExecute;
    }

    public void Execute()
    {
        if (this.OnPreExecute != null)
        {
            this.OnPreExecute();
        }

        // create the http request
        HttpWebRequest httpWebRequest = WebRequest.CreateHttp(this.Url);
        httpWebRequest.Method = "GET";
        httpWebRequest.Accept = "application/json";

        // get the response asynchronously
        httpWebRequest.BeginGetResponse(OnGetResponseCompleted, httpWebRequest);
    }

    private void OnGetResponseCompleted(IAsyncResult ar)
    {
        var httpWebRequest = (HttpWebRequest)ar.AsyncState;

        // get the response
        HttpWebResponse response;
        try
        {
            response = (HttpWebResponse)httpWebRequest.EndGetResponse(ar);
        }
        catch (WebException)
        {
            this.InvokeOnErrorHandler("Unable to connect to the web page.");
            return;
        }
        catch (Exception e)
        {
            this.InvokeOnErrorHandler(e.Message);
            return;
        }

        if (response.StatusCode != HttpStatusCode.OK)
        {
            this.InvokeOnErrorHandler((int)response.StatusCode + " " + response.StatusDescription);
            return;
        }

        // response stream
        var stream = response.GetResponseStream();

        // deserialize json
        var jsonSerializer = new DataContractJsonSerializer(typeof(T));
        var responseObject = (T)jsonSerializer.ReadObject(stream);

        // call the virtual method
        this.InvokeInUiThread(() => this.OnPostExecute(responseObject));
    }

    private void InvokeOnErrorHandler(string message)
    {
        if (this.OnError != null)
        {
            this.InvokeInUiThread(() => this.OnError(message));
        }
    }

    private void InvokeInUiThread(Action action)
    {
        Deployment.Current.Dispatcher.BeginInvoke(action);
    }

    public string Url { get; private set; }

    public Action<T> OnPostExecute { get; private set; }

    public Action OnPreExecute { get; set; }

    public Action<string> OnError { get; set; }
}

You can execute this task by specifying Url and the OnPostExecute callback. Also you can set other properties. Example:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        Cities = new ObservableCollection<CityViewModel>();

        string url = "http://api.geonames.orgzz/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=en&username=vortexwolf";
        
        var task = new HttpGetTask<CitiesList>(url, this.OnPostExecute);
        task.OnPreExecute = this.OnPreExecute;
        task.OnError = this.OnError;

        task.Execute();
    }

    private void OnPreExecute()
    {
        this.IsLoading = true;
    }

    private void OnPostExecute(CitiesList responseObject)
    {
        this.OnCitiesDownloaded(responseObject);
        this.IsLoading = false;
    }

    private void OnError(string message)
    {
        MessageBox.Show(message);
        this.IsLoading = false;
    }

    public ObservableCollection<CityViewModel> Cities { get; set; }

    private bool _isLoading;

    public bool IsLoading
    {
        get { return _isLoading; }
        set
        {
            _isLoading = value;
            RaisePropertyChanged("IsLoading");
        }
    }

    private void OnCitiesDownloaded(CitiesList citiesList)
    {
        var cityModels = citiesList.Cities
            .Select(c =>
                    new CityViewModel
                    {
                        Name = c.Name,
                        CountryCode = c.CountryCode,
                        Population = c.Population
                    })
            .ToList();

        cityModels.ForEach(this.Cities.Add);
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

I created the sample application for Windows Phone 7 which retrieves JSON data and displays it on the phone.

The source code of the example you can download here: PhoneJsonTest.zip