Blog: Time to reflect

Keyboard selection on Silverlight ListBox and ComboBox

Added by Fons Sonnemans on 07-Feb-2010

Silverlight doesn't support keyboard selection on a ListBox or Combox. I have created a small Behavior which fixes this problem. You can attach the KeyboardSelectionBehavior to a ListBox or ComboBox using Microsoft Expression Blend. You drag it from the Assets and drop it on your ComboBox or ListBox. If you have a custom ItemTemplate you will have to set the SelectionMemberPath property.

Try my behavior below. If you press a key on the ComboBox or ListBoxes it will select the next item starting with the given key.

Get Microsoft Silverlight

The ComboBox in this example is not databound, The behavior uses the Convert.ToString() method to convert the Content of each ListBoxItem/ComboBoxItem to a string. An invariant case insensitive StartWith() comparison is used to find the next item.

The left ListBox is databound to SampleData containing Employees. The behavior uses the DisplayMemberPath of the ListBox. The Name of the databound Employee is used for keyboard selection.

<ListBox Margin="79,64,0,23" DisplayMemberPath="Name"

    ItemsSource="{Binding Employees}" HorizontalAlignment="Left" Width="176">

    <i:Interaction.Behaviors>

        <local:KeyboardSelectionBehavior />

    </i:Interaction.Behaviors>

</ListBox>

The right ListBox is also databound but has a custom ItemTemplate. We can't use the DisplayMemberPath. The behavior has a SelectionMemberPath property to specify which property to use for selection. In this example it is set to the Name of the databound Employee.

<ListBox Margin="272,23,23,23"

    ItemsSource="{Binding Employees}">

    <i:Interaction.Behaviors>

        <local:KeyboardSelectionBehavior SelectionMemberPath="Name"/>

    </i:Interaction.Behaviors>

    <ListBox.ItemTemplate>

        <DataTemplate>

            <StackPanel Orientation="Horizontal">

                <Image Source="{Binding Image}" Height="32" Width="32"/>

                <StackPanel>

                    <TextBlock Text="{Binding Name}" FontWeight="Bold"/>

                    <TextBlock Text="{Binding Description}"/>

                </StackPanel>

            </StackPanel>

        </DataTemplate>

    </ListBox.ItemTemplate>

</ListBox>

Here is the code:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Controls.Primitives;

using System.Windows.Data;

using System.Windows.Input;

using System.Windows.Interactivity;

 

namespace ReflectionIT.Silverlight.Behaviors {

 

    /// <summary>

    /// This behavior can be attached to a ListBox or ComboBox to

    /// add keyboard selection

    /// </summary>

    public class KeyboardSelectionBehavior : Behavior<Selector> {

 

        /// <summary>

        /// Gets or sets the Path used to select the text

        /// </summary>

        public string SelectionMemberPath { get; set; }

 

        public KeyboardSelectionBehavior() {}

 

        /// <summary>

        /// Attaches to the specified object: subscribe on KeyDown event

        /// </summary>

        protected override void OnAttached() {

            base.OnAttached();

            this.AssociatedObject.KeyDown += DoKeyDown;

        }

 

        void DoKeyDown(object sender, KeyEventArgs e) {

 

            // Create a list of strings and indexes

            int index = 0;

            IEnumerable<Item> list = null;

            var path = SelectionMemberPath ??

                this.AssociatedObject.DisplayMemberPath;

            var evaluator = new BindingEvaluator();

            if (path != null) {

                list = this.AssociatedObject.Items.OfType<object>()

                    .Select(item => {

                        // retrieve the value using the supplied Path

                        var binding = new Binding();

                        binding.Path = new PropertyPath(path);

                        binding.Source = item;

 

                        BindingOperations.SetBinding(evaluator,

                            BindingEvaluator.TargetProperty, binding);

                        var value = evaluator.Target;

 

                        return new Item { Index = index++,

                                Text = Convert.ToString(value) };

                    });

            } else {

                list = this.AssociatedObject.Items.OfType<ListBoxItem>()

                    .Select(item => new Item { Index = index++,

                                Text = Convert.ToString(item.Content) });

            }

            // Sort the list starting at next selectedIndex to the end and

            // then from the beginning

            list = list.OrderBy(item => item.Index <=

                this.AssociatedObject.SelectedIndex ?

                item.Index + this.AssociatedObject.Items.Count : item.Index);

 

            // Find first starting with

            var text = e.Key.ToString();

            var first = list.FirstOrDefault(item => item.Text.StartsWith(text,

                StringComparison.InvariantCultureIgnoreCase));

            if (first != null) {

                // found

                this.AssociatedObject.SelectedIndex = first.Index;

            }

        }

 

        /// <summary>

        /// Helper class

        /// </summary>

        private class Item {

            public int Index;

            public string Text;

        }

 

        /// <summary>

        /// Helper class used for property path value retrieval

        /// </summary>

        private class BindingEvaluator : FrameworkElement {

 

            public static readonly DependencyProperty TargetProperty =

                DependencyProperty.Register(

                    "Target",

                    typeof(object),

                    typeof(BindingEvaluator), null);

 

            public object Target {

                get { return GetValue(TargetProperty); }

                set { SetValue(TargetProperty, value); }

            }

        }

 

    }

}

You can download the code including the sample application here.


Silverlight Behaviors and Commands

Added by Fons Sonnemans on 21-Dec-2009

A few months ago I wrote an blog post about a Silverlight 3.0 LetItSnowBehavior. This Behavior can be used to add a Snow effect to a Canvas. Very usefull if you want to create a christmas card.

This behavior was always showing you falling snow flakes. You couldn't stop and (re)start the gameloop. The best way to implement this is by adding Commands to the behavior. This allows you to select one or more triggers to Start or Stop the gameloop. I have added the Start and Stop properties of the type ICommand to the LetItSnowBehavior class. In the constructor I have initialized these properties with new ActionCommand objects (Microsoft.Expression.Interactions.dll) and delegates to the OnStop() and OnStart() methods. Triggers attached to these commands will execute the these methods.

public class LetItSnowBehavior : Behavior<Canvas> {


    private DispatcherTimer _gameLoop = new DispatcherTimer();


    public LetItSnowBehavior() {

        // Create Commands

        this.Start = new ActionCommand(this.OnStart);

        this.Stop = new ActionCommand(this.OnStop);


        // Init timer

        _gameLoop.Interval = new TimeSpan(0, 0, 0, 0, 10);

        _gameLoop.Tick += new EventHandler(gameLoop_Tick);

        _gameLoop.Start();

    }


    public ICommand Start { get; private set; }


    public ICommand Stop { get; private set; }


    private void OnStart() {

        _gameLoop.Start();

    }


    private void OnStop() {

        _gameLoop.Stop();

    }


    ...

You can use Expression Blend to attach triggers to the Commands. Select the LetItSnowBehavior object and open it properties. Click the + button for the Start property and select an EventTrigger. Select the 'buttonStart' as source and the 'Click' as event. You can even add multiple triggers to the command.

Set Triggers on LetItSnowBehavior in Expression Blend 3.0

You can download the code from here.

Watch my Silverlight Christmas card

Added by Fons Sonnemans on 20-Dec-2009

Click the following image to see my Christmas card which I have created using Silverlight 3.0 and Expression Blend.

Click to view this Christmas card

I didn't have to write any line of  C# code. I only re-used some of my existing behaviors: LetItSnowBehavior and ControlMediaElementAction.


Beschikbaar voor Silverlight projecten

Added by Fons Sonnemans on 07-Dec-2009

Op dit moment ben ik als Silverlight ontwikkelaar op zoek naar een project. Bent u op zoek naar een ervaren silverlight specialist/architect neem dan contact met mij op. Zie ook mijn CV voor extra infromatie over mij.


Simple ReportDocument for Silverlight 4

Added by Fons Sonnemans on 25-Nov-2009

I have written an Simple Report Library for Windows Forms applications a few years ago. The new Printing API makes it possible to create a similar solution for Silverlight 4.

You create a report by instantiating a new ReportDocument object. You can set the Title and the SubTitle. Next you add Paragraphs (FrameworkElements) to the report. Finally you Print the report.

ReportDocument r = new ReportDocument() {

    Title = "Test Title",

    SubTitle = "Test SubTitle",

};


for (int i = 0; i < 40; i++) {


    var tb = new TextBlock() {

        Text = "Test text " + i,

        FontSize = i + 10,

    };


    r.Paragraphs.Add(tb);

}


r.Print();

This prints the following Test Title.pdf if you print it to a PDF writer.

Test Title.pdf

The real magic is in the dp_PrintPage event handler of the ReportDocument class. The Header and the Paragraphs are added to a StackPanel. Before a paragraph is added the height (actual or the measured desired) is compared with the available space. If it doesn't fit the page is full and ready to be printed. The PageVisual is set to the StackPanel. The next page will continue with the current paragraph.

private void pd_PrintPage(object sender, PrintPageEventArgs e) {

    PageNumber++;

    if (_panel.Children.Count == 0) {

        _panel.Children.Add(Header);

    } else {

        // remove all except header

        while (_panel.Children.Count > 1) {

            _panel.Children.RemoveAt(1);

        }

    }

    _visual.Height = e.PrintableArea.Height;

    _visual.Width = e.PrintableArea.Width;


    double totalHeight = e.PrintableArea.Height - this.Margin.Top - this.Margin.Bottom;

    double totalWidth = e.PrintableArea.Width - this.Margin.Left - this.Margin.Right;


    double height;

    if (_header.ActualHeight > 0) {

        height = Header.ActualHeight;

    } else {

        Header.Measure(new Size(totalWidth, totalHeight));

        height = Header.DesiredSize.Height;

    }


    while (_currentParagraphIndex < Paragraphs.Count) {

        var paragraph = Paragraphs[_currentParagraphIndex];

        if (paragraph.ActualHeight > 0) {

            height += paragraph.ActualHeight;

        } else {

            var size = new Size(totalWidth, totalHeight - height);

            paragraph.Measure(size);

            height += paragraph.DesiredSize.Height;

        }

        if (height > totalHeight) {

            // doesn't fit anymore

            break;

        }

        _currentParagraphIndex++;

        _panel.Children.Add(paragraph);

    }


    e.PageVisual = _visual;

    e.HasMorePages = _currentParagraphIndex < Paragraphs.Count;

}

I hope you like it. I will discuss creating custom headers in a next post. You can download the code from Codeplex.


SilverlightXP 2.0 release

Added by Fons Sonnemans on 24-Nov-2009

Visit SilverlightXP

SilverlightXP is a web application where Silverlight Developers and Designers can post links to controls, resources and other interesting information about Microsoft Silverlight.

AUTHORS:

SilverlightXP is a collaboration between Loek van den Ouweland and Fons Sonnemans. The goal was to create a great User Experience for the Silverlight Community when they search for the latest and greatest stuff about Silverlight.

TECHNICAL:

SilverlightXP is a Silverlight 3 application created with Visual Studio 2008, Expression Design 3 and Expression Blend 3. Used techniques include .NET RIA services and LINQ. A combination of URL rewriting, Silverlight Navigation Framework and ASP.NET SEO-optimization was used for optimal Google integration.

FUTURE:

SilverlightXP was built for all of us in the Silverlight Community and we would love to hear from you. If you have ideas, comments, found errors or other things you would like to share with us, please send an email to feedback@silverlightxp.net


Silverlight Flip Clock

Added by Fons Sonnemans on 04-Nov-2009

I have created a simple Flip Clock using Silverlight 3 and Expression Blend 3. It uses some simple animations to flip the hours, minutes and seconds. I hope you like it.

Get Microsoft Silverlight

You can download the sourcecode from here.


Control a MediaElement using a custom Behavior

Added by Fons Sonnemans on 11-Oct-2009

Controlling a MediaElement in Silverlight isn't difficult. You use the Play(), Stop() and Pause() methods in your code. I have written the 'ControlMediaElementAction' Behavior which makes it even easier. You don't have to write a single line of code. The ControlMediaElementAction is associated with a MediaElement. It has a ControlMediaElementOption which you can set to Play, Stop, Pause and RewindAndPlay. The Invoke() methods controls (Plays, Stops, Pauses and RewindAndPlays) the AssociatedObject (MediaElement).

public class ControlMediaElementAction : TriggerAction<MediaElement> {


    protected override void Invoke(object o) {

        switch (ControlMediaElementOption) {

            case ControlMediaElementOption.Play:

                this.AssociatedObject.Play();

                break;

            case ControlMediaElementOption.Stop:

                this.AssociatedObject.Stop();

                break;

            case ControlMediaElementOption.Pause:

                this.AssociatedObject.Pause();

                break;

            case ControlMediaElementOption.RewindAndPlay:

                this.AssociatedObject.Position = TimeSpan.Zero;

                this.AssociatedObject.Play();

                break;

            default:

                break;

        }

    }


    public ControlMediaElementOption ControlMediaElementOption { get; set; }


}


public enum ControlMediaElementOption {

    Play, Stop, Pause, RewindAndPlay

}

You assign a ControlMediaElementAction to a MediaElement. In Expression Blend you drag it from you Asset tab and drop it on a MediaElement. Then you can select your trigger and set all other properties from the Properties tab.

ControlMediaElementAction in Blend 3.0

In the following example I have 3 ControlMediaElementAction assigned to a MediaElement. The first is triggerd by the 'Click' event of 'buttonPlay' and uses the 'Play' option. The second is triggerd by the 'Click' event of 'buttonPause' and uses the 'Pause' option. The third is triggerd by the 'MediaEnded' event of the MediaElement and uses the 'RewindAndPlay' option, making the movie loop.

<UserControl

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

   xmlns:local="clr-namespace:SilverlightApplication7"

    x:Class="SilverlightApplication7.MainPage"

    Width="640" Height="480">

 

    <Grid x:Name="LayoutRoot" Background="White">

        <MediaElement x:Name="SL_wmv" Height="200" HorizontalAlignment="Left"

                 Margin="160,40,0,0" Width="160" Source="/SL.wmv"

                 Stretch="Fill" AutoPlay="False">

            <i:Interaction.Triggers>

                <i:EventTrigger SourceName="buttonPlay" EventName="Click">

                    <local:ControlMediaElementAction/>

                </i:EventTrigger>

                <i:EventTrigger SourceName="buttonPause" EventName="Click">

                    <local:ControlMediaElementAction

                       ControlMediaElementOption="Pause"/>

                </i:EventTrigger>

                <i:EventTrigger EventName="MediaEnded">

                    <local:ControlMediaElementAction

                       ControlMediaElementOption="RewindAndPlay"/>

                </i:EventTrigger>

            </i:Interaction.Triggers>

        </MediaElement>

        <Button x:Name="buttonPlay" Height="40" Margin="160,0,0,160"

               Width="160" Content="Play"/>

        <Button x:Name="buttonPause" Height="40" Margin="160,0,0,80"

               Width="160" Content="Pause"/>

    </Grid>

</UserControl>

You can download the sourcecode here.


Silverlight XP.net

Added by Fons Sonnemans on 01-Sep-2009

I'm proud to announce the Silverlight XP.net website. It is a web application where Silverlight Developers can post links to interesting information, controls, resources e.t.c. We invite you to submit your Silverlight resources.

Silverlight XP.net is a Silverlight 3.0 LOB application which uses a lot of the new techniques:

  • .NET Ria Services
  • Navigation Application (deeplinking + history)
  • Search Engine Optimization (SEO)
  • Behaviors

Silverlight XP was created by Loek van den Ouweland and me, and is currently at version 1.0. We plan to add a lot of features soon. We don’t have a feedback-function yet. Please drop comments about the website by mail.


Microsoft Silverlight 3 training

Added by Fons Sonnemans on 17-Aug-2009

Silverlight is een 'cross-over browser plugin' van Microsoft die het mogelijk maakt om animaties, audio en video weer te geven in de webbrowser (zoals bijvoorbeeld Explorer, Firefox, Safari).

Silverlight bevat een subset van het .NET 3.5 Framework waarmee, vanuit een Microsoft .NET taal zoals C#, Visual Basic of .NET, een programma geschreven kan worden dat in een browser draait. Tot nu toe kon dat alleen met JavaScript of met Flash van Adobe.

Reflection IT is de eerste opleider met een Microsoft Silverlight 3 training.

Docenten Fons Sonnemans en Loek van den Ouweland verzorgen deze interactieve en praktijkgerichte trainingen voor zowel geinteresseerden als startende en meer ervaren programmeurs en grafisch vormgevers.


« Previous Entries  Next Entries »