Monday, April 30, 2012

LocationServices for Windows Phone, part 5: exploring surrounding POI's from Nokia Places

before we begin

Get the API keys set up – if you haven’t registered already, go to http://api.maps.nokia.com/en/restplaces/overview.html and do so, then register for a personal app key & code that you can use. After that’s done, edit the App.xaml and insert your own keys.

        <usoniandream:ServiceURI x:Key="NOKIA_SERVICE_URI_PLACES" URL="http://places.nlp.nokia.com/places/v1/" />

        <usoniandream:ServiceAPIKey x:Key="NOKIA_APP_CODE" Value="" />
        <usoniandream:ServiceAPIKey x:Key="NOKIA_APP_ID" Value="" />

the namespace you’ll need to add to your App.xaml is:

            xmlns:usoniandream="clr-namespace:Usoniandream.WindowsPhone.LocationServices.Models;assembly=Usoniandream.WindowsPhone.LocationServices"

NuGet for the prerequisites

Easiest way to grab both the Reactive Extensions (rx) and RestSharp is to use NuGet. Install the following two packages and you’re set to move forward:

  • Rx_Experimental-Xaml version 1.1.11111
  • RestSharp version 102.7

add the references needed

1) Add references to the core library:

  • Usoniandream.WindowsPhone.LocationsServices
  • Usoniandream.WindowsPhone.Extensions
  • Usoniandream.WindowsPhone.GeoConverter

2) Add a reference to the Bing data library:

  • Usoniandream.WindowsPhone.LocationsServices.Nokia

add some code

3) Declare a service layer:

        public LocationServices.Service.Nokia.Reactive.ServiceLayer nokiaservicelayer = new LocationServices.Service.Nokia.Reactive.ServiceLayer();

4) Declare a property container for the results:

        public ObservableCollection<Place> NokiaPlaces { get; set; }

5) Wire the call to get the POI’s nearby:

            var places = nokiaservicelayer.GetNokiaPlaces(new WindowsPhone.LocationServices.SearchCriterias.Nokia.Places.Places(CurrentLocation, "sv, en-gb;q=0.8, en;q=0.7"))
                .DistinctUntilChanged()
                .ObserveOnDispatcher()
                .Subscribe(x =>
                    {
                        NokiaPlaces.Add(x);
                    },
                    ex =>
                    {
                        // handle error..
                    });

6) Finally, a bit of xaml to wrap it all up:

                    <toolkit:MultiselectList x:Name="mslNokiaPlaces" ItemsSource="{Binding NokiaPlaces}" Margin="-20,0,0,0" Visibility="{Binding NokiaPlaces.Count, Converter={StaticResource NumberToVisibilityConverter}}">
                        <toolkit:MultiselectList.ItemTemplate>
                            <DataTemplate>
                                <Button Style="{StaticResource ChromelessButton}" DataContext="{Binding .}">
                                    <StackPanel>
                                        <TextBlock x:Name="Title" Foreground="#004C9A" Text="{Binding Content}"  TextTrimming="WordEllipsis" Style="{StaticResource PhoneTextLargeStyle}" />
                                            <TextBlock Text="sponsored result" Style="{StaticResource PhoneTextSubtleStyle}" Visibility="{Binding Sponsored, Converter={StaticResource BoolToVisibilityConverter}}"/>
                                        <TextBlock Text="{Binding Distance, Converter={StaticResource DoubleToDistanceConverter}}" Style="{StaticResource PhoneTextNormalStyle}" />
                                    </StackPanel>
                                </Button>
                            </DataTemplate>
                        </toolkit:MultiselectList.ItemTemplate>
                        <toolkit:MultiselectList.ItemContainerStyle>
                            <Style TargetType="toolkit:MultiselectItem">
                                <Setter Property="Margin" Value="-12,0" />
                                <Setter Property="CacheMode" Value="BitmapCache" />
                            </Style>
                        </toolkit:MultiselectList.ItemContainerStyle>
                    </toolkit:MultiselectList>

7) And the end result gives us:

image

8) from there you can easily add a click handler to the surrounding button that goes out to fetch the details in a similar way as shown below:

        public PlaceDetails NokiaPlaceDetails { get; set; }

        public void FetchDetails(LocationServices.Models.Nokia.Places.Place item)
        {
            NokiaService.GetNokiaPlace(new LocationServices.SearchCriterias.Nokia.Places.Place(item.Id, "sv, en-gb;q=0.8, en;q=0.7"))
            .ObserveOnDispatcher()
            .Subscribe(x =>
            {
                NokiaPlaceDetails = x;
            },
                ex =>
                {
                    // handle errors..
                },
                () =>
                {
                    this.RaisePropertyChanged("");
                });
        }

9) and the end result of that place detail query could look something like this after you’ve hammered it into a xaml view:

image

9) and finally, if you instead want to display your results on a map it’s as easy as binding the collection to a MapItemsControl:

    <maps:Map ZoomBarVisibility="Visible" ScaleVisibility="Visible" ZoomLevel="15" Center="{Binding CurrentLocation}" CredentialsProvider="{StaticResource BingCredentials}">
    <maps:MapItemsControl ItemsSource="{Binding NokiaPlaces}">
        <maps:MapItemsControl.ItemTemplate>
        <DataTemplate>
            <maps:Pushpin Background="#004C9A" Content="{Binding Content}" Location="{Binding Location}" />
        </DataTemplate>
        </maps:MapItemsControl.ItemTemplate>
    </maps:MapItemsControl>
    <maps:Pushpin Location="{Binding CurrentLocation}" Style="{StaticResource MyLocationStyle}" />
    </maps:Map>

The BingCredentials is your Bing API key that the Maps control require in order to not show the “invalid credentials” and the MyLocationStyle is simply a style for the pushpin in the shape of an ellipsis.

10) with the neat result as seen below:

image

The framework libraries, source code and sample app are all on GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

Sunday, April 29, 2012

LocationServices for Windows Phone, part 4: getting the current address from Bing

before we begin

Get the API keys set up – if you haven’t registered already, go to http://www.bingmapsportal.com/ and do so, then register for a personal app key that you can use. After that’s done, edit the App.xaml and insert your own key.

        <usoniandream:ServiceAPIKey x:Key="BING_API_KEY" Value="" />
        <usoniandream:ServiceURI x:Key="BING_SERVICE_URI_LOCATION" URL="http://dev.virtualearth.net/REST/v1/" />

the namespace you’ll need to add to your App.xaml is:

            xmlns:usoniandream="clr-namespace:Usoniandream.WindowsPhone.LocationServices.Models;assembly=Usoniandream.WindowsPhone.LocationServices"

NuGet for the prerequisites

Easiest way to grab both the Reactive Extensions (rx) and RestSharp is to use NuGet. Install the following two packages and you’re set to move forward:

  • Rx_Experimental-Xaml version 1.1.11111
  • RestSharp version 102.7

add the references needed

1) Add references to the core library:

  • Usoniandream.WindowsPhone.LocationsServices
  • Usoniandream.WindowsPhone.Extensions
  • Usoniandream.WindowsPhone.GeoConverter

2) Add a reference to the Bing data library:

  • Usoniandream.WindowsPhone.LocationsServices.Bing

add some code

3) Declare a service layer:

        public LocationServices.Service.Bing.Reactive.ServiceLayer bingservicelayer = new LocationServices.Service.Bing.Reactive.ServiceLayer();

4) Declare a property for the address:

        public LocationServices.Models.Bing.BingMapLocation CurrentAddress { get ; set;  }

5) Wire the call to get the address:

            addressSubscriber = bingservicelayer.GetAddressAtPoint(new LocationServices.SearchCriterias.Bing.AddressByPoint(GeoHelper.Watcher.Position.Location))
                .ObserveOnDispatcher()
                .Subscribe(result =>
                {
                    this.CurrentAddress = result;
                },
                ex =>
                {
                    // handle errors..
                });

6) Finally, a bit of xaml to wrap it all up:

      <toolkit:PhoneTextBox x:Name="tbAddress" Text="{Binding CurrentAddress.FormattedAddress}" ActionIconTapped="tbAddress_ActionIconTapped" ActionIcon="/content/appbar.globe.nomargin.png" />

7) Along with a click event:

The GetCurrentAddressByCurrentLocation() method contains the code above we just added.

        private void tbAddress_ActionIconTapped(object sender, EventArgs e)
        {
            ViewModel.WireUpPositioning();
            ViewModel.GetCurrentAddressByCurrentLocation();
        }

8) And the end result gives us:

image

The framework libraries, source code and sample app are all on GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

LocationServices for Windows Phone, part 3: the setup

As stated in previous posts the entire framework is built to be observable using Reactive Extensions (Rx).

Consider this as a simple scenario:

“Get me all known motorcycle parking places in Stockholm within a radius of 500 meters from my current location”

First things first – add the core references:

  • Usoniandream.WindowsPhone.LocationServices
  • Usoniandream.WindowsPhone.Extensions
  • Usoniandream.WindowsPhone.GeoConverter
  • Next up, add a reference to the data library for Stockholm:

  • Usoniandream.WindowsPhone.LocationServices.Stockholm
  • Now, for the ViewModel code parts..

    from the top, a collection is needed:

     public ObservableCollection<Models.Stockholm.ParkingLocation> ParkingLocations { get; set; }

    next, a routine to wire that collection to the library and the underlying API:

                var rxlocations = sthlmservicelayer.GetParkingLocationsByRadius(new SearchCriterias.Stockholm.Parking.ParkingLocation.ParkingLocationsByRadius(500, Models.Enums.Stockholm.VehicleTypeEnum.Bike))
                    .ObserveOnDispatcher()
                    .Subscribe(
                    // result
                        x =>
                        {
                            ParkingLocations.Add(x);
                        },
                    // exception
                        ex =>
                        {
                            // handle exception..
                        });
    

    And that’s it – the ParkingLocations collection is ready to go!

    how does it work?

    First, there’s the ServiceLayer which acts as the point of entry to the framework.

    namespace Usoniandream.WindowsPhone.LocationServices.Service.Stockholm.Reactive
    {
        public class ServiceLayer : LocationServices.Service.Reactive.GenericServiceLayer
        {
        }
    }
    

    In each ServiceLayer (there’s one for observable calls, and one using standard RestSharp async pattern) the methods are listed one after the other:

            public IObservable<Models.Stockholm.ParkingLocation> GetParkingLocationsByRadius(SearchCriterias.Stockholm.Parking.ParkingLocation.ParkingLocationsByRadius criteria)
            {
                return ExecuteRequestReturnObservable<Models.Stockholm.ParkingLocation, Models.JSON.Stockholm.ParkingPlaces.RootObject>(criteria);
            }
    

    These use one of (currently) four available methods found in the base class GenericServiceLayer:

            public IObservable<Ttarget> ExecuteRequestReturnObservable<Ttarget, Tsource>(SearchCriterias.ISearchCriteria<Ttarget, Tsource> criteria) where Tsource : new()
            {
                if (String.IsNullOrWhiteSpace(criteria.Client.BaseUrl))
                {
                    throw new ArgumentException("missing 'baseurlresourcename', please check App.xaml.", "baseurlresourcename");
                }
                if (String.IsNullOrWhiteSpace(criteria.APIkey))
                {
                    throw new ArgumentException("missing api key, please check App.xaml.", "APIkey");
                }
    
                return criteria.Client.ExecuteAsync<Tsource>(criteria.Request)
                                .SelectMany<Tsource, Ttarget>(x => criteria.Mapper.JSON2Model(x));
            }
    
    

    Using Reactive Extensions and a mapper (see here for an example of a mapper) the JSON response is transformed to a local model representation. The in param for each method is of type SearchCriteria which simply acts as a strong-typed request definition while in turn holds key data such as endpoint url,parameters and more.

    namespace Usoniandream.WindowsPhone.LocationServices.Models.Stockholm
    {
        public class ParkingLocation : ILocation
        {
            public virtual GeoCoordinate Location { get; set; }
            public virtual object Content { get; set; }
    
            public List<List<double>> Coordinates { get; set; }
    
            public string CityDistrict { get; set; }
            public int ParkingTimeTotalMinutes 
            {
                get
                {
                    int total = (MaxDays.GetValueOrDefault(0) * 24 * 60) +
                        (MaxHours.GetValueOrDefault(0) * 60) +
                        MaxMinutes.GetValueOrDefault(0);
                    if (total > 0)
                    {
                        return total;
                    }
                    else
                    {
                        return int.MaxValue;
                    }
                }
            }
            public int? MaxDays { get; set; }
            public int? MaxHours { get; set; }
            public int? MaxMinutes { get; set; }
            public string OtherInfo { get; set; }
            public int? Meters { get; set; }
            public string Type { get; set; }
            public string StartWeekDay { get; set; }
        }
    }
    

    The SearchCriteria mentioned above is also structured in a fairly generic manner:

    namespace Usoniandream.WindowsPhone.LocationServices.SearchCriterias.Stockholm.Parking.ParkingLocation
    {
        public class ParkingLocationsByRadius : ParkingLocationsBase
        {
            public ParkingLocationsByRadius(int radius, Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum vehicletype)
                : base(vehicletype)
            {
                Radius = radius;
    
                Request.Resource += "within?";
                
                Request.AddParameter("radius", Radius);
                Request.AddParameter("lat", Location.Latitude.ToString().Replace(",", "."));
                Request.AddParameter("&lng",Location.Longitude.ToString().Replace(",", "."));
            }
    
            public GeoCoordinate Location { get; set; }
            public int Radius { get; private set; }
        }
    }
    

    as you might notice, the criteria above inherits a ParkingsLocationBase, here’s what that looks like:

    namespace Usoniandream.WindowsPhone.LocationServices.SearchCriterias.Stockholm.Parking.ParkingLocation
    {
        public abstract class ParkingLocationsBase : SearchCriteriaBase<Models.Stockholm.ParkingLocation, Usoniandream.WindowsPhone.LocationServices.Models.JSON.Stockholm.ParkingPlaces.RootObject>
        {
            public ParkingLocationsBase(Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum vehicletype)
                : base("STHLM_DATA_SERVICE_URI_PARKINGLOCATION")
            {
                Mapper = new Usoniandream.WindowsPhone.LocationServices.Mappers.Stockholm.Parking.ParkingLocation();
                APIKeyResourceName = "STHLM_DATA_API_KEY_PARKING";
                VehicleType = vehicletype;
    
                switch (this.VehicleType)
                {
                    case Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum.Car:
                        Request.Resource += "ptillaten/";
                        break;
                    case Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum.Bike:
                        Request.Resource += "pmotorcykel/";
                        break;
                    case Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum.Truck:
                        Request.Resource += "plastbil/";
                        break;
                    case Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum.Handicap:
                        Request.Resource += "prorelsehindrad/";
                        break;
                    default:
                        throw new ArgumentException("VehicleType must be set", "VehicleType");
                }
    
                Request.AddParameter("outputFormat", "json");
                Request.AddParameter("apiKey", APIkey);
            }
    
            public Usoniandream.WindowsPhone.LocationServices.Models.Enums.Stockholm.VehicleTypeEnum VehicleType { get; private set; }
        }
    }

    And, in turn, the ParkingLocationBase inherits from SearchCriteriaBase which adds the following to the mix:

        public abstract class SearchCriteriaBase<Ttarget, Tsource> : INotifyPropertyChanged, Usoniandream.WindowsPhone.LocationServices.SearchCriterias.ISearchCriteria<Ttarget, Tsource>
        {
            public SearchCriteriaBase()
            {
                Request = new RestRequest();
                Client = new RestClient();
            }
            public SearchCriteriaBase(string baseurlresourcename)
            {
                Request = new RestRequest();
                if (string.IsNullOrWhiteSpace(baseurlresourcename))
                {
                    throw new ArgumentException("missing 'baseurlresourcename', please check App.xaml.", "baseurlresourcename");
                }
                Client = new RestClient(((Models.ServiceURI)Application.Current.Resources[baseurlresourcename]).URL);
            }
            public SearchCriteriaBase(Method method)
            {
                Request = new RestRequest(method);
                Client = new RestClient();
            }
            public SearchCriteriaBase(Method method, string baseurlresourcename)
            {
                Request = new RestRequest(method);
                if (string.IsNullOrWhiteSpace(baseurlresourcename))
                {
                    throw new ArgumentException("missing 'baseurlresourcename', please check App.xaml.", "baseurlresourcename");
                }
                Client = new RestClient(((Models.ServiceURI)Application.Current.Resources[baseurlresourcename]).URL);
            }
    
            public RestClient Client { get; private set; }
            public RestRequest Request { get; private set; }
    
            public IMapper<Ttarget, Tsource> Mapper { get; set; }
            
            public Dictionary<string, string> Parameters { get; set; }
    
            public virtual string APIKeyResourceName { get; protected set; }
    
            private string apikey = null;
    
            public virtual string APIkey 
            { 
                get
                {
                    if (string.IsNullOrWhiteSpace(apikey))
                    {
                        if (!String.IsNullOrWhiteSpace(APIKeyResourceName))
                        {
                            return ((Models.ServiceAPIKey)Application.Current.Resources[APIKeyResourceName]).Value;
                        }
                        if (!string.IsNullOrWhiteSpace(apikey))
                        {
                            return apikey;
                        }
                        else
                        {
                            throw new ArgumentException("missing api key, please check App.xaml.", "APIkey");
                        }
                    }
                    return apikey;
                } 
                private set
                {
                    apikey = value;
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
        }
    

    So, there you have it. The engine parts that more or less make up the majority of the framework.

    The framework libraries, source code and sample app are all on GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

    Saturday, April 28, 2012

    LocationServices for Windows Phone, part 2: the basics

    As stated in the previous post the framework uses  Reactive Extensions (Rx) and RestSharp as glue in order to wire up a framework that is observable, asynchronous and easy to use.

    Regardless of what data parts of the framework you want to use you’ll need to reference the following three libraries (further on referred to as the “core libraries”):

    • Usoniandream.WindowsPhone.Extensions
    • Usoniandream.WindowsPhone.GeoConverter
    • Usoniandream.WindowsPhone.LocationServices

    Using the various data parts of the framework is as easy as referencing the library you want to use - either one, some, or all of them. At the time of this post the data parts consist of:

    • Usoniandream.WindowsPhone.LocationServices.Bing
    • Usoniandream.WindowsPhone.LocationServices.Goteborg
    • Usoniandream.WindowsPhone.LocationServices.Nokia
    • Usoniandream.WindowsPhone.LocationServices.Stockholm
    • Usoniandream.WindowsPhone.LocationServices.Stockholm.AR

    Keep in mind that you’ll need to get your own API keys for the data API’s you want to use – that is, if you want to use the Bing location functionality library you’ll need a Bing Maps API key, and so on. All API keys are added to the applications App.xaml file (see sample project for more details).

    Once you’ve referenced the core libraries, any data library and set up your API key you’re set to go!

    Each library holds a ServiceLayer class (both classic async with callback and a Reactive Extensions based one). Within a ServiceLayer are the methods available for each of the API’s. The list of available methods are far from complete, they’re not at all the entirety of what each API has to offer – here you’re more than welcome to contribute to the project.

    It’s easy to familiarize yourself with the layout of the framework and it’s namespace structure. The main points of interest on the consumer end of the framework is the Usoniandream.WindowsPhone.LocationServices.Models namespace and the Usoniandream.WindowsPhone.LocationServies.Service namespace

    Short story:

    1. Add the references needed
    2. Set the API keys needed in App.xaml
    3. Declare an instance of a servicelayer
    4. Fire away at the metods available

    The framework libraries, source code and sample app are all on GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

    LocationServices for Windows Phone part 1: the background

    While creating the windows phone apps “Stockholm Parking” and “Stockholm Explorer” I figured why not build a framework while at it so others looking to add this data to their windows phone apps will have an easy start?

    After settling on a structure of using Reactive Extensions (Rx) and RestSharp it was merely a matter of wiring up a framework that I could expand as the apps progressed.

    Not long after that I first started the framework I also published “Stockholm Parking” and came to thinking that the city of Göteborg could also benefit from such an app, and so I added the Goteborg data part as well. Following that I added Nokia Places since it’s used in “Stockholm Explorer”..

    Today the framework consists of two parts: core functionality and data functionality.

    Core libraries:

    • Usoniandream.WindowsPhone.Extensions
    • Usoniandream.WindowsPhone.GeoConverter
    • Usoniandream.WindowsPhone.LocationServices

    Data libraries:

    • Usoniandream.WindowsPhone.LocationServices.Bing
    • Usoniandream.WindowsPhone.LocationServices.Goteborg
    • Usoniandream.WindowsPhone.LocationServices.Nokia
    • Usoniandream.WindowsPhone.LocationServices.Stockholm
    • Usoniandream.WindowsPhone.LocationServices.Stockholm.AR

    The posts following this is meant to serve as a tutorial and history of ho to use this framework, what it contains, and why it’s here.

    The framework libraries, source code and sample app are all on GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

    Monday, April 23, 2012

    Introducing Usoniandream.WindowsPhone.LocationServices for Windows Phone

    GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

    In a series of posts following this one I’ll be explaining the framework structure, data sources, usage and some samples.

    combined
    Included in the framework is a simple Windows Phone 7.1 application which shows a simple sample usage scenario.

    Usoniandream.WindowsPhone.LocationServices

    LocationServices for Windows Phone, part 1: the background
    LocationServices for Windows Phone, part 2: the basics
    LocationServices for Windows Phone, part 3: the setup

    Usoniandream.WindowsPhone.LocationServices.Bing

    LocationServices for Windows Phone, part 4: getting the current address from Bing

    Usoniandream.WindowsPhone.LocationServices.Nokia

    LocationServices for Windows Phone, part 5: exploring surrounding POI's from Nokia Places

    Usoniandream.WindowsPhone.LocationServices.Goteborg

    LocationServices for Windows Phone, part 6: getting nearby parking places in Göteborg
    LocationServices for Windows Phone, part 7: getting nearby parking meters in Göteborg

    Usoniandream.WindowsPhone.LocationServices.Stockholm

    LocationServices for Windows Phone, part 8: getting nearby parking places in Stockholm
    LocationServices for Windows Phone, part 9: getting nearby parking meters in Stockholm
    LocationServices for Windows Phone, part 10: getting service units in Stockholm
    LocationServices for Windows Phone, part 11: getting places in Stockholm

    Advanced topics

    LocationServices for Windows Phone, part 12: combining the results from several sources
    LocationServices for Windows Phone, part 13: making use of Reactive Extensions to fire multiple queries at once
    LocationServices for Windows Phone, part 14: extending the framework with your own models and mappers

    Stay tuned for a lot more..

    Additions made 2012-05-01

    • Usoniandream.WindowsPhone.LocationServices.Commute
    • Usoniandream.WindowsPhone.LocationServices.Orebro

    GitHub: https://github.com/peterdrougge/Usoniandream.WIndowsPhone.LocationServices

    Monday, April 09, 2012

    Reactive Extensions & WPF - Hiding mouse pointer when idle

    Ever wanted to easily hide the mouse pointer after x seconds of idle activity in your WPF application? There are many ways to accomplish this, here is one way you might find useful: the MouseIdle Behavior extension.

    This attachable behavior uses Reactive Extensions (Rx) to handle mouse movement and a normal DispatcherTimer to Control the interval routine.

    Usage is as simple as adding the namespace to the declaration of your window:

    xmlns:extensions="clr-namespace:Usoniandream.WPF.Extensions;assembly=Usoniandream.WPF.Extensions"
    

    After that all you have to do is add the code to the interactivity block, like this:

     <i:Interaction.Behaviors>
            <extensions:MouseIdleBehavior
                    TimeToIdle="3000" />
     i:Interaction.Behaviors>
    

    The complete source is available for download at the following url: https://github.com/peterdrougge/Code-Samples/blob/master/WPF/Behaviors/MouseIdleBehavior.cs

    Maybe you'll find this useful :)

    P.