Drawing on a SilverStream map
This tutorial will walk you through the creation of a Silverlight map that you can draw on. This tutorial is one of many Telogis SilverStream tutorials. Three methods of drawing will be demonstrated:
- hard-coded XAML drawings
- hard-coded C# drawings
- using the mouse cursor to draw a shape
This tutorial is broken into the following steps:
- Create a Visual Studio project
- Configure a GeoStream server using C# code
- Add a map using XAML code
- Implement drawing functionality using C# code
Create a Silverlight project and configure a GeoStream server
Make sure that the file clientaccesspolicy.xml exists in your Inetpub\wwwroot directory. Create a new Silverlight project named 'draw on map' and add a reference to Telogis.GeoBase.SilverStream.dll. Be sure to configure a GeoStream server.
See the Create a SilverStream-Specific Visual Studio Project tutorial for an illustrated walk-through.
Adding User Controls
Open MainPage.xaml (found in Solution Explorer, under the 'draw on map' project) and add the following attribute to the UserControl tag. This attribute allows your XAML code to access the Telogis.GeoBase.Silverlight namespace.
xmlns:GeoBase="clr-namespace:Telogis.GeoBase;assembly=Telogis.GeoBase.Silverlight" xmlns:GeoBaseShape="clr-namespace:Telogis.GeoBase.Silverlight;assembly=Telogis.GeoBase.Silverlight"
Adding these attributes to your XAML code is similar to adding using namespace directives in C# code. These attributes map the GeoBase and GeoBaseShape XAML namespaces to the Telogis.GeoBase and Telogis.GeoBase.SilverLight CLR namespaces (found in Telogis.GeoBase.SilverStream.dll).
Copy and paste the following code snippet between the Grid tags. This code creates five grid rows. The first four rows are used for our controls:
- Map, with a predefined LineString already drawn
- Clear, draw and pan buttons. These present basic functionality options to the user
- Color selector, allows the user to choose a drawing color
- Pattern selector, allows the user to choose a drawing pattern
<Grid.RowDefinitions>
<RowDefinition Height="400" /> <!-- map -->
<RowDefinition Height="30" /> <!-- clear/draw/pan buttons -->
<RowDefinition Height="30" /> <!-- color selector -->
<RowDefinition Height="30" /> <!-- pattern selector -->
<RowDefinition Height="*" /> <!-- vertical fill -->
</Grid.RowDefinitions>
<Border BorderBrush="Black"
Grid.Row="0"
BorderThickness="2">
<GeoBase:Map x:Name="MainMap"
Center="34,-118"
MouseLeftButtonDown="MainMap_MouseLeftButtonDown"
MouseLeftButtonUp="MainMap_MouseLeftButtonUp"
MouseMove="MainMap_MouseMove"
MouseLeave="MainMap_MouseLeave"
Zoom="6">
<!--LineStrings can be created entirely in XAML-->
<GeoBaseShape:LineString x:Name="Squiggle"
Points="34.369073,-118.106250 34.361334,-118.101562 34.357465,-118.101562 34.345855,-118.092188 34.341984,-118.092188 34.334243,-118.087500 34.322631,-118.078125 34.314887,-118.064062 34.295527,-118.054688 34.287781,-118.045312 34.283909,-118.035938 34.276162,-118.031250 34.276162,-118.021875 34.268415,-118.007812 34.260666,-117.989062 34.260666,-117.979688 34.260666,-117.970312 34.260666,-117.946875 34.260666,-117.932812 34.264541,-117.909375 34.268415,-117.900001 34.268415,-117.890625 34.276162,-117.876562 34.280036,-117.867188 34.280036,-117.857812 34.287781,-117.843751 34.287781,-117.825001 34.287781,-117.810938 34.287781,-117.796875 34.287781,-117.792188 34.283909,-117.768751 34.276162,-117.754688 34.264541,-117.735938 34.252919,-117.721875 34.252919,-117.712499 34.245169,-117.693749 34.233544,-117.679688 34.233544,-117.670312 34.233544,-117.665625 34.221917,-117.646875 34.214164,-117.628125 34.214164,-117.618749 34.210288,-117.614062 34.210288,-117.599999 34.202535,-117.571875 34.202535,-117.557812 34.202535,-117.543750 34.202535,-117.539062 34.206411,-117.520312 34.214164,-117.506250 34.221917,-117.482812 34.241294,-117.450000 34.252919,-117.431250 34.260666,-117.407812 34.268415,-117.389062 34.283909,-117.356250 34.291654,-117.342187 34.291654,-117.328125 34.295527,-117.314063 34.299399,-117.304687 34.303272,-117.295313 34.311016,-117.276563 34.311016,-117.262500 34.314887,-117.234375 34.314887,-117.210937 34.314887,-117.196875 34.314887,-117.173437 34.311016,-117.154687 34.299399,-117.135937 34.295527,-117.126563 34.291654,-117.126563 34.291654,-117.117187 34.283909,-117.112500 34.272288,-117.093750 34.268415,-117.075000 34.260666,-117.051563 34.260666,-117.032813 34.260666,-117.000000 34.260666,-116.985937 34.260666,-116.953124 34.260666,-116.934374 34.260666,-116.920313 34.260666,-116.906250 34.260666,-116.892187 34.260666,-116.878124 34.264541,-116.859374">
<GeoBaseShape:LineString.Stroke>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Color="Red" Offset="0" />
<GradientStop Color="Blue" Offset="0.1" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.5" />
<GradientStop Color="Red" Offset="0.75" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</GeoBaseShape:LineString.Stroke>
</GeoBaseShape:LineString>
</GeoBase:Map>
</Border>
<!-- Three buttons, laid out horizontally: 'Clear', 'Draw' and 'Move Map' -->
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Button Content="Clear" Click="Button_Click" />
<!-- this button is initially disabled because we init the map in 'draw' mode -->
<Button x:Name="btnDraw" IsEnabled="False" Click="btnDraw_Click" Content="Draw" />
<Button x:Name="btnPan" Click="btnPan_Click" Content="Move Map" />
</StackPanel>
<!-- Color selector combo-box -->
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBlock Margin="5,1" VerticalAlignment="Center">Color</TextBlock>
<ComboBox x:Name="cbColors" Width="55" Height="25" SelectedIndex="2">
<Rectangle Width="20" Height="10" Fill="Red" />
<Rectangle Width="20" Height="10" Fill="Green" />
<Rectangle Width="20" Height="10" Fill="Blue" />
<Rectangle Width="20" Height="10" Fill="Yellow" />
<Rectangle Width="20" Height="10" Fill="Purple" />
<Rectangle Width="20" Height="10" Fill="Gray" />
<Rectangle Width="20" Height="10" Fill="Black" />
<Rectangle Width="20" Height="10" Fill="White" />
</ComboBox>
</StackPanel>
<!-- Pattern selector combo-box -->
<StackPanel Orientation="Horizontal" Grid.Row="3">
<TextBlock Margin="5,1" VerticalAlignment="Center">Stroke Pattern</TextBlock>
<ComboBox x:Name="cbStripe" Width="150" Height="25" SelectedIndex="0">
<Line X1="0" Y1="0" X2="200" Y2="0" Stroke="Black" StrokeThickness="5" />
<Line X1="0" Y1="0" X2="200" Y2="0" Stroke="Black" StrokeThickness="5" StrokeDashArray="1,1" />
<Line X1="0" Y1="0" X2="200" Y2="0" Stroke="Black" StrokeThickness="5" StrokeDashArray="3,1" />
<Line X1="0" Y1="0" X2="200" Y2="0" Stroke="Black" StrokeThickness="5" StrokeDashArray="5,1" />
</ComboBox>
</StackPanel>
Implementing Drawing Functionality
Open MainPage.xaml.cs and add add the following two using directives at the top of the source file:
using Telogis.GeoBase; using Telogis.GeoBase.Silverlight;
Add these two member properties to the MainPage class.
bool drawing = false; LineString currentLine;
The drawing flag will be used to track when the user is drawing a line on the map: when drawing is true the user is drawing, when false the user is not drawing. The currentLine property represents the line that is being drawn on the map.
Initializing the Web Page
We must allow the user to switch between drawing and panning behavior. When in drawing mode the mouse will draw on the map, when in panning mode the mouse will move and zoom the map. The XAML buttons (created in the previous section) will be used to switch between these two modes. The Clear button will remove all drawings from the map.
Each Map object has an R-Tree to manage objects that it owns. In this tutorial we are unlikely to have a large number of items on the map so we decrease overhead by instructing the Map object to not create an R-Tree. We do this by setting the RtreeThreshold property to zero.
This constructor will also define two shapes: a large red "Z" and a green spiral. These shapes will be drawn on the map when the page loads. You should compare this method of creating drawings with the XAML method (demonstrated in the previous section, "Adding User Controls" to create a colored squiggle).
public MainPage() {
InitializeComponent();
MainMap.LockMap = true; // we start in 'draw' mode, so we should prevent the map from moving
MainMap.RtreeThreshold = 0; // don't create an rtree to manage map objects
// create a large red "Z" on the map
LineString bigZ = new LineString();
bigZ.Color = Colors.Red;
bigZ.Points.Add(new LatLon("35,-116"));
bigZ.Points.Add(new LatLon("35,-118"));
bigZ.Points.Add(new LatLon("36,-116"));
bigZ.Points.Add(new LatLon("36,-118"));
// create a large green spiral on the map
LineString spiral = new LineString();
LatLonCollection spiralPoints = new LatLonCollection();
for (double i = 0; i <= 10; i += .05) {
spiralPoints.Add(new LatLon((Math.Sin(i * 2) * (i / 5)) + 40, (Math.Cos(i * 2) * (i / 5)) - 118));
}
spiral.Color = Colors.Green;
spiral.Points = spiralPoints;
MainMap.Items.Add(spiral);
MainMap.Items.Add(bigZ);
}
Clear, Draw and Pan Buttons
We must allow the user to switch between drawing and panning behavior. When in drawing mode the mouse will draw on the map, when in panning mode the mouse will move and zoom the map. The XAML buttons (created in the previous section) will be used to switch between these two modes. The Clear button will remove all drawings from the map.
// Clear button - clears all drawings from the map
private void Button_Click(object sender, RoutedEventArgs e) {
MainMap.Items.Clear();
}
// Draw button - prevent the map from moving
private void btnDraw_Click(object sender, RoutedEventArgs e) {
btnPan.IsEnabled = true;
btnDraw.IsEnabled = false;
MainMap.LockMap = true;
}
// Pan button - allow the map to move
private void btnPan_Click(object sender, RoutedEventArgs e) {
btnPan.IsEnabled = false;
btnDraw.IsEnabled = true;
MainMap.LockMap = false;
}
Drawing on the Map
Each drawing on the map is represented as a LineString object. When the left mouse button is pressed down on the map we create a new LineString object and set drawing to true.
The drawing variable defines our current state (true for drawing, false for panning.
The color and pattern of the LineString object are selected from the cbColors and cbStripe combo boxes.
// when the left button is pressed we begin drawing a new line
private void MainMap_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
drawing = true;
currentLine = new LineString();
currentLine.Stroke = (cbColors.SelectedItem as Rectangle).Fill;
System.Windows.Media.DoubleCollection temp = new System.Windows.Media.DoubleCollection();
foreach (double d in (cbStripe.SelectedItem as System.Windows.Shapes.Line).StrokeDashArray){
temp.Add(d);
}
currentLine.StrokeDashArray = temp;
MainMap.Items.Add(currentLine);
}
// when the user releases the mouse button we should stop drawing
private void MainMap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
drawing = false;
}
// when the mouse button is held down and the mouse is moving we should
// add more points to our current line
private void MainMap_MouseMove(object sender, MouseEventArgs e) {
// ensure the map is locked and we are in drawing mode
// (we will only be in drawing mode if the mouse button is held down)
if (MainMap.LockMap && drawing) {
LatLon ll = MainMap.XYOverlaytoLatLon(e.GetPosition(MainMap));
currentLine.Points.Add(ll);
}
}
// when the user moves the mouse off the map we should stop drawing
private void MainMap_MouseLeave(object sender, MouseEventArgs e) {
drawing = false;
}
Testing
Press F5 to build and run your application. Your default web browser will load a web page displaying a map and color/pattern selectors. Use the buttons to toggle between drawing and panning modes, or clear drawings from the map.

Next step
The next tutorial, Drawing polygons on a SilverStream map, demonstrates how to use C# code to draw predefined shapes over a SilverStream map. Alternatively you can skip ahead to another Telogis SilverStream tutorial.
Tagged under: silverlight maps