Skip to content
March 28, 2011 / slimcode

Late-binding, Bing Maps and Pushpins with an unknown position

It seems the Bing Maps controls for Windows Phone 7 have a little problem when you use late-binding (setting the data context in the Loaded event, for example) and you have a Pushpin with a Location set to GeoCoordinate.Unknown. For example, take this Pushpin declaration:

  1. <map:Pushpin Location="{Binding UserPosition}" Content="you" Visibility="{Binding UserPosition, Converter={StaticResource GeoCoordinateVisibilityConverter}}" />

 

Even if you change the pushpin’s visibility to Collapsed for unknown locations, you end up with an ugly exception:

System.InvalidOperationException occurred:
  Message=UIElement.Arrange(finalRect) cannot be called with Infinite or NaN values in finalRect.

Well, that’s great info, right? The workaround I found was to insert that pushpin in a layer, and collapse the layer for the same criteria:

  1. <map:MapLayer Visibility="{Binding UserPosition, Converter={StaticResource GeoCoordinateVisibilityConverter}}">
  2.     <map:Pushpin Location="{Binding UserPosition}" Content="you" />
  3. </map:MapLayer>

 

A little overhead, but it works.

February 7, 2011 / slimcode

GeoTrax missing link – Do it yourself!

WP_000105With the two GeoTrax sets we have, I often get to a point where I’m missing one or two half-length straight tracks. I’ve looked at acquiring them on eBay et al, but it becomes very expensive for only those two pieces. Today, I realized that the missing part was barely longer than the length of a clothespin. Hmmm, interesting. It turns out that wagons only need one side of the rails to be flat (the inner part). And the small missing gap isn’t a problem, the crossing track already has gaps and it doesn’t affect the wagons. Could I split a wooden pin in two and use these parts as rails?

Yes, quite easily!

First, you take a half-length track and draw the contour on cardboard. You cut out the piece, making sure to remove the pen width too, else it will be difficult to fit into the real pieces. You might have to clue together two layers of cardboard if its too thin. The cardboard and the pin’s width must equal the original rail’s thickness. In my case, I cut two pieces and glued them together.

WP_000097 WP_000098 WP_000099

Then you split the clothespin in two, discarding the spring. I measured the inner distance between the rails on the original track. It’s 30 mm or 1” 3/16. You draw lines on the cardboard piece for that distance, making sure these are centered. In my case, the lines were 17 mm from the edge (5/8”), though it was more like 16 mm on the original piece.

WP_000100 WP_000101

You put some glue on the edge of each wood piece, and place them carefully, so that their flat side lies on the line, the uneven side pointing outside. Once the second piece is placed, take the ruler before the glue dries, and adjust it so you get 30 mm (or 31 mm, better more than less) between the pieces.

WP_000102

Let it dry a few minutes. You now have a nice half-length track replacement part. Sure, it doesn’t clip into other pieces, it just slides into them. But most track layouts stay in place even with such a weakest link.

WP_000103 WP_000104

You can now make those crossing tracks patterns you couldn’t with only two half-length rails. Hurray!

WP_000106

WP_000107

September 11, 2010 / slimcode

Detect when a ListBox scrolls to its end (WP7)

I wanted to fetch and add new items to a ListBox when it scrolls to its end, to reproduce infinite scrolling.

Fellow tweep @GaryGJohnson pointed me to the ScrollViewer’s VerticalOffset, ExtentHeight and ViewportHeight, which would give me all the maths I needed.

Unfortunately, there it no scrolling events available, either on the ListBox or the inner ScrollViewer. From what I understand, I could have received change notifications directly from the ScrollViewer’s VerticalOffset property if I were to work with SL4, but not with SL3 on Windows Phone 7. My friend Jacques came to the rescue! He suggested I create myself a custom DependencyProperty with a changed notification, and bind it to the ScrollViewer’s VerticalOffset.

 

My ListBox declaration
<ListBox x:Name="businessesListBox"
         ItemsSource="{Binding Listings}"
         ItemTemplate="{StaticResource ListingItemTemplate}"
         SelectionChanged="businessesListBox_SelectionChanged"
         Style="{StaticResource BusinessListBoxStyle}" />

 

From Blend, I right-clicked my ListBox, chose “Edit Template” –> “Edit a copy” to add that BusinessListBoxStyle, and added a handler for the ScrollViewer’s Loaded event.

 

My ListBox’ Style
<Style x:Key="BusinessListBoxStyle" TargetType="ListBox">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBox">
                <ScrollViewer x:Name="scrollViewer"
                              BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}"
                              Background="{TemplateBinding Background}"
                              Foreground="{TemplateBinding Foreground}"
                              Padding="{TemplateBinding Padding}"
                              Loaded="ScrollViewer_Loaded">
                    <ItemsPresenter/>
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

I created a custom property in my PhoneApplicationPage.

 

My custom property
public static readonly DependencyProperty ListVerticalOffsetProperty = DependencyProperty.Register(
  "ListVerticalOffset",
  typeof( double ),
  typeof( SearchBusinessResultsPage ),
  new PropertyMetadata( new PropertyChangedCallback( OnListVerticalOffsetChanged ) ) );

public double ListVerticalOffset
{
  get { return ( double )this.GetValue( ListVerticalOffsetProperty ); }
  set { this.SetValue( ListVerticalOffsetProperty, value ); }
}

 

From the ScrollViewer’s Loaded handler, I just need to bind my custom property to to the ScrollViewer’s VerticalOffset. Notice I keep a reference on the ScrollViewer, since it’s in a ContentTemplate.

 

ScrollViewer’s Loaded handler
private ScrollViewer _listScrollViewer;

private void ScrollViewer_Loaded( object sender, RoutedEventArgs e )
{
  _listScrollViewer = sender as ScrollViewer;

  Binding binding = new Binding();
  binding.Source = _listScrollViewer;
  binding.Path = new PropertyPath( "VerticalOffset" );
  binding.Mode = BindingMode.OneWay;
  this.SetBinding( ListVerticalOffsetProperty, binding );
}

 

The final job is to verify how far the scrolling is every time my custom property changes (thus every time the ScrollViewer’s VerticalOffset changes). I also make sure not to trigger updates more than once per ScrollableHeight.

 

Scrolled to the bottom?
private double _lastFetch;

private static void OnListVerticalOffsetChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
{
  SearchBusinessResultsPage page = obj as SearchBusinessResultsPage;
  ScrollViewer viewer = page._listScrollViewer;

  if( viewer != null )
  {
    if( page._lastFetch < viewer.ScrollableHeight )
    {
      // Trigger within 1/4 the viewport.
      if( viewer.VerticalOffset >= viewer.ScrollableHeight – viewer.ViewportHeight / 4 )
      {
        page._lastFetch = viewer.ScrollableHeight;
        this.ViewModel.FetchNextPage();
      }
    }
  }
}

 

As you can see, you can adjust how close to the end you trigger an update. There is one final gotcha you much be warned of. Make sure your “FetchNextPage” equivalent prevents reentrancy, just in case adding a new item to your bound collection causes the OnListVerticalOffset event to fire (we never know) and another fetch is triggered.

Follow

Get every new post delivered to your Inbox.