A line symbol uses the path stroke property to define the main color, so it's not possible to get a border to the line with only one path element. One approach might be to define a symbol with many paths having various colors and thicknesses.Something like:
<esri:SimpleLineSymbol x:Key="myExtendedLineSymbol" Color="Blue" Width="10" >
<esri:SimpleLineSymbol.ControlTemplate>
<ControlTemplate>
<Grid>
<Grid.Resources>
<local:CloneGeometryConverter x:Key="cloneConverter" />
<esri:MultiplicationConverter x:Key="multiplicationConverter" />
</Grid.Resources>
<Path x:Name="Element" Stroke="{Binding Symbol.Color}" StrokeThickness="{Binding Symbol.Width}"
StrokeEndLineCap="Round" StrokeStartLineCap="Round"/>
<Path Stroke="White" StrokeThickness="{Binding Symbol.Width, Converter={StaticResource multiplicationConverter}, ConverterParameter=0.6}"
StrokeEndLineCap="Round" StrokeStartLineCap="Round"
Data="{Binding Path=Data, ElementName=Element, Converter={StaticResource cloneConverter}}"
/>
<Path Stroke="Red" StrokeThickness="{Binding Symbol.Width, Converter={StaticResource multiplicationConverter}, ConverterParameter=0.2}"
StrokeEndLineCap="Round" StrokeStartLineCap="Round"
Data="{Binding Path=Data, ElementName=Element, Converter={StaticResource cloneConverter}}"
/>
</Grid>
</ControlTemplate>
</esri:SimpleLineSymbol.ControlTemplate>
</esri:SimpleLineSymbol>
which gives the attached screenshot as result. Important note : I am not sure this approach is fully supported by ESRI because the root element of the line symbol is not a path. So despite it looks working well at first glance, consider this as an unsupported approach without any ESRI guarantee. You will also need code to clone a geometry (without cloning it we get a crash).Here is the code I found on the Web:
public class CloneGeometryConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType == typeof(System.Windows.Media.Geometry) && value is System.Windows.Media.Geometry)
return Clone(value as System.Windows.Media.Geometry);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
// Code coming from http://kiwigis.blogspot.com/2010/06/cloning-path-geometry-in-silverlight.html
private static object Clone(Object obj)
{
// Get all properties and clone them.
PropertyInfo[] properties = obj.GetType().GetProperties();
object cloneObj = obj.GetType().GetConstructors()[0].Invoke(null);
foreach (PropertyInfo property in properties)
{
object value = property.GetValue(obj, null);
if (value != null)
{
if (IsPresentationFrameworkCollection(value.GetType()))
{
object collection = property.GetValue(obj, null);
int count = (int)collection.GetType().GetProperty("Count").GetValue(collection, null);
for (int i = 0; i < count; i++)
{
// Get each child of the collection.
object child = collection.GetType().GetProperty("Item").GetValue(collection, new Object[] { i });
object cloneChild = Clone(child);
object cloneCollection = property.GetValue(cloneObj, null);
collection.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, cloneCollection, new object[] { cloneChild });
}
}
// If the property is a UIElement, we also need to clone it.
else if (value is UIElement)
{
object obj2 = property.PropertyType.GetConstructors()[0].Invoke(null);
Clone(obj2);
property.SetValue(cloneObj, obj2, null);
}
// For a normal property, its value doesn't need to be // cloned. So just copy its value to the new object.
else if (property.CanWrite)
{
property.SetValue(cloneObj, value, null);
}
}
}
return cloneObj;
}
private static bool IsPresentationFrameworkCollection(Type type)
{
if (type == typeof(object))
{
return false;
}
if (type.Name.StartsWith("PresentationFrameworkCollection"))
{
return true;
}
return IsPresentationFrameworkCollection(type.BaseType);
}
}