pondělí 30. května 2011

Silverlight DataForm ItemsSource is null when DataForm is not visible

This is just a quick post:

When DataForm is not visible, then its ItemsSource collection is null. This can make sense, but it may also cause problems, imagine the following:

The DataForm is in Expander. On a click of a button you want to expand the Expander and add new item to the DataForm:

MyExpander.IsExpanded = true;
//this call passes
MyDataForm.AddNewItem();

//but here CurrentItem will be null
var vm = MyDataForm.CurrentItem as MyViewModel;

//and here you will get NullPointerException
vm.MyProperty = "value";

The problem is that when you set IsExpanded to true, the UI does not get updated right by the way, so you will have to force it by calling this.UpdateLayout(). After this the ItemsSource collection will be binded to whatever enumeration you have set in the XAML and the next call of AddNewItem() will set the CurrentItem property of DataForm to a non-null value.

VS 2010 CLR20r3 crash

When working on a Silverlight project I have encountered the following crash and I was unable to open my solution in Visual Studio.

Visual Studion 2010 Crash
Problem Event Name: CLR20r3
Core error: NullRefferenceException

As the solution was composed of several project I have decided to remove from the solution file one by one and this way see which project was causing the error.
I was quite lucky, it was the first on, that I have tried - the Silverligt project.

Here is the structure of VS 2010 solution file (.SLN file):

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{Project GUID") = "Project name1", "Project reference1", "{Reference GUID}"
EndProject
...
...
Project("{Project GUID") = "Project name2", "Project reference2", "{Reference GUID}"
EndProject
Global
...runtime configurations etc.
EndGlobal

So it is quite easy to remove one project reference just by deleting the Project - EndProject part.

Note that if your solution is under Team Foundation Server source control, the solution file might be read-only so you will have to change that in the properties of the file.

Once you have removed the project causing the error you can just easily add it to the solution from Visual Studio (Add Existing Project).

Hope it helps.

úterý 17. května 2011

Bing Maps - Bindable TilesLayer

Specifying secondary tiles source allows you to cover parts of the map with you another layer. This layer might contain some additional information, such as new routes or points or any other geolocated information. In Silverlight you can use secondary tiles source by using the TilesLayer and LocationRectTileSource elements.

With Bing Maps and Silverlight you would basically do it like this:

<map:Map CredentialsProvider="..." Mode="Road" LogoVisibility="Collapsed"
                x:Name="map">
<map:TilesLayer x:Name="layer">
</map:TilesLayer>
</map:Map>

And in codebehind:

layer.TileSources.Clear();
LocationRectTileSource source = new LocationRectTileSource();
source.UriFormat = viewModel.TilesURL;
source.BoundingRectangle = new LocationRect(viewModel.leftCorner, viewModel.rightCorner);
source.ZoomRange = new Range(10, 18);
layer.TileSources.Add(source);

That works just fine, nevertheless it would be nice to do all of this in xml markup, in other words use data binding to populate UriFormat and Bounding Rectangle properties.

However some properties in the Bing Maps framework are not defined as DependencyProperties but as classical CLR properties. This is the case of UriFormat of LocationRectTileSource.

When you try to bind whatever string to this property, than you will obtain XML parsing error during run-time.

The similar situation happens for example for Stroke property of MapPolyline, which is probably more common issue. Here is a fine solution for that one.

But back to the LocationRectTileSource. When there is property which is not DependencyProperty and you would still like to bind data to this property you can always write some kind of wrapper class.

Because I have always only one TilesLayer with one LocationRectTileSource, I have decided to inherit my wrapping component directly from TilesLayer.

namespace CustomControls {
public class CustomTilesLayer : MapTileLayer
{
    public static readonly DependencyProperty TilesURLProperty = DependencyProperty.Register("TilesURL", typeof(String),
            typeof(CustomTilesLayer), new PropertyMetadata(new PropertyChangedCallback(TilesURLPropertyChanged)));


    public String TilesURL
    {
        get { return (String)GetValue(TilesURLProperty); }
        set { SetValue(TilesURLProperty, value); }
    }

    private static void TilesURLPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        CustomTilesLayer layer = sender as CustomTilesLayer;
        layer.TileSources.Clear();
LocationRectTileSource source = new LocationRectTileSource();
        source.UriFormat = a.NewValue;
        source.BoundingRectangle = new LocationRect(leftCorner,rightCorner);
        source.ZoomRange = new Range(10, 18);
        layer.TileSources.Add(source);
    }
}
}

Now you can use this component in your map the following way:
<usercontrol x:Class="YourClass"
xmlns:controls="clr-namespace:CustomControls">
<map:Map CredentialsProvider="..." Mode="Road" LogoVisibility="Collapsed"
                x:Name="map">
<controls:CustomTilesLayer TilesURL="{Binding TilesURL}" Opacity="0.8"/>
</map:Map>
</UserControl>