diff --git a/MapControl/MapItem.cs b/MapControl/MapItem.cs
index df8fb8e7..052b8da0 100644
--- a/MapControl/MapItem.cs
+++ b/MapControl/MapItem.cs
@@ -43,16 +43,9 @@ namespace MapControl
IsEnabledChanged += IsEnabledPropertyChanged;
}
- public event RoutedEventHandler Selected
+ public Map ParentMap
{
- add { AddHandler(SelectedEvent, value); }
- remove { RemoveHandler(SelectedEvent, value); }
- }
-
- public event RoutedEventHandler Unselected
- {
- add { AddHandler(UnselectedEvent, value); }
- remove { RemoveHandler(UnselectedEvent, value); }
+ get { return MapPanel.GetParentMap(this); }
}
public bool IsSelected
@@ -66,9 +59,16 @@ namespace MapControl
get { return MapItemsControl.GetIsCurrent(this); }
}
- public Map ParentMap
+ public event RoutedEventHandler Selected
{
- get { return MapPanel.GetParentMap(this); }
+ add { AddHandler(SelectedEvent, value); }
+ remove { RemoveHandler(SelectedEvent, value); }
+ }
+
+ public event RoutedEventHandler Unselected
+ {
+ add { AddHandler(UnselectedEvent, value); }
+ remove { RemoveHandler(UnselectedEvent, value); }
}
protected override void OnMouseEnter(MouseEventArgs e)
diff --git a/MapControl/MapItemsControl.cs b/MapControl/MapItemsControl.cs
index cacd1a50..24ad37aa 100644
--- a/MapControl/MapItemsControl.cs
+++ b/MapControl/MapItemsControl.cs
@@ -40,6 +40,11 @@ namespace MapControl
Items.CurrentChanged += OnCurrentItemChanged;
}
+ public Map ParentMap
+ {
+ get { return MapPanel.GetParentMap(this); }
+ }
+
public SelectionMode SelectionMode
{
get { return (SelectionMode)GetValue(SelectionModeProperty); }
@@ -74,7 +79,7 @@ namespace MapControl
public IList GetItemsInGeometry(Geometry geometry)
{
- return GetItemsInGeometry(geometry, new ArrayList(Items.Count), Items.Count);
+ return GetItemsInGeometry(geometry, new ArrayList());
}
protected override bool IsItemItsOwnContainerOverride(object item)
@@ -111,15 +116,39 @@ namespace MapControl
{
e.Handled = true;
UIElement container = (UIElement)sender;
+ UIElement selectedContainer;
- if (SelectionMode == SelectionMode.Extended &&
- (Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift)) == 0)
+ if (SelectionMode != SelectionMode.Extended || (Keyboard.Modifiers & ModifierKeys.Control) != 0)
+ {
+ Selector.SetIsSelected(container, !Selector.GetIsSelected(container));
+ }
+ else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
{
SelectedItem = GetItem(container);
}
- else
+ else if ((selectedContainer = GetContainer(SelectedItem)) != null)
{
- Selector.SetIsSelected(container, !Selector.GetIsSelected(container));
+ ViewportPosition p1 = MapPanel.GetViewportPosition(selectedContainer);
+ ViewportPosition p2 = MapPanel.GetViewportPosition(container);
+
+ if (p1 != null && p2 != null)
+ {
+ Rect rect = new Rect(p1.Position, p2.Position);
+
+ BeginUpdateSelectedItems();
+ SelectedItems.Clear();
+ SelectedItems.Add(SelectedItem);
+
+ foreach (object item in Items)
+ {
+ if (item != SelectedItem && IsItemInRect(item, rect))
+ {
+ SelectedItems.Add(item);
+ }
+ }
+
+ EndUpdateSelectedItems();
+ }
}
}
@@ -161,46 +190,68 @@ namespace MapControl
{
if (SelectionMode == SelectionMode.Single)
{
- IList items = GetItemsInGeometry(geometry, new ArrayList(1), 1);
- SelectedItem = items.Count > 0 ? items[0] : null;
+ SelectedItem = GetFirstItemInGeometry(geometry);
}
else
{
BeginUpdateSelectedItems();
- GetItemsInGeometry(geometry, SelectedItems, Items.Count);
+ SelectedItems.Clear();
+ GetItemsInGeometry(geometry, SelectedItems);
EndUpdateSelectedItems();
}
}
}
- private IList GetItemsInGeometry(Geometry geometry, IList items, int maxItems)
+ private object GetFirstItemInGeometry(Geometry geometry)
{
- items.Clear();
-
if (!geometry.IsEmpty())
{
foreach (object item in Items)
{
- UIElement container = GetContainer(item);
-
- if (container != null)
+ if (IsItemInGeometry(item, geometry))
{
- ViewportPosition viewportPosition = MapPanel.GetViewportPosition(container);
+ return item;
+ }
+ }
+ }
- if (viewportPosition != null && geometry.FillContains(viewportPosition.Position))
- {
- items.Add(item);
+ return null;
+ }
- if (items.Count >= maxItems)
- {
- break;
- }
- }
+ private IList GetItemsInGeometry(Geometry geometry, IList items)
+ {
+ if (!geometry.IsEmpty())
+ {
+ foreach (object item in Items)
+ {
+ if (IsItemInGeometry(item, geometry))
+ {
+ items.Add(item);
}
}
}
return items;
}
+
+ private bool IsItemInGeometry(object item, Geometry geometry)
+ {
+ UIElement container = GetContainer(item);
+ ViewportPosition viewportPosition;
+
+ return container != null
+ && (viewportPosition = MapPanel.GetViewportPosition(container)) != null
+ && geometry.FillContains(viewportPosition.Position);
+ }
+
+ private bool IsItemInRect(object item, Rect rect)
+ {
+ UIElement container = GetContainer(item);
+ ViewportPosition viewportPosition;
+
+ return container != null
+ && (viewportPosition = MapPanel.GetViewportPosition(container)) != null
+ && rect.Contains(viewportPosition.Position);
+ }
}
}
diff --git a/MapControl/Pushpin.cs b/MapControl/Pushpin.cs
index 4c48ad07..e2bf7812 100644
--- a/MapControl/Pushpin.cs
+++ b/MapControl/Pushpin.cs
@@ -23,6 +23,11 @@ namespace MapControl
typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin)));
}
+ public Map ParentMap
+ {
+ get { return MapPanel.GetParentMap(this); }
+ }
+
public Location Location
{
get { return (Location)GetValue(LocationProperty); }
diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs
index 111a8cc6..d1eb3e76 100644
--- a/MapControl/TileImageLoader.cs
+++ b/MapControl/TileImageLoader.cs
@@ -19,10 +19,11 @@ namespace MapControl
///
/// Loads map tile images by their URIs and optionally caches the images in an ObjectCache.
///
- public class TileImageLoader : DispatcherObject
+ public class TileImageLoader
{
private readonly TileLayer tileLayer;
private readonly ConcurrentQueue pendingTiles = new ConcurrentQueue();
+ private int downloadThreadCount;
///
/// Default Name of an ObjectCache instance that is assigned to the Cache property.
@@ -90,7 +91,7 @@ namespace MapControl
newTiles.ForEach(tile =>
{
- Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = imageTileSource.GetImage(tile.XIndex, tile.Y, tile.ZoomLevel)));
+ tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = imageTileSource.GetImage(tile.XIndex, tile.Y, tile.ZoomLevel)));
});
}
else
@@ -106,19 +107,19 @@ namespace MapControl
newTiles.ForEach(tile =>
{
string key = CacheKey(tile);
- byte[] imageBuffer = Cache.Get(key) as byte[];
+ byte[] buffer = Cache.Get(key) as byte[];
- if (imageBuffer == null)
+ if (buffer == null)
{
pendingTiles.Enqueue(tile);
}
- else if (!CreateTileImage(tile, imageBuffer))
+ else if (!CreateTileImage(tile, buffer))
{
// got corrupted buffer from cache
Cache.Remove(key);
pendingTiles.Enqueue(tile);
}
- else if (IsCacheOutdated(imageBuffer))
+ else if (IsCacheOutdated(buffer))
{
// update cached image
outdatedTiles.Add(tile);
@@ -128,28 +129,31 @@ namespace MapControl
outdatedTiles.ForEach(tile => pendingTiles.Enqueue(tile));
}
- int numDownloads = Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads);
-
- while (--numDownloads >= 0)
+ while (downloadThreadCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads))
{
+ Interlocked.Increment(ref downloadThreadCount);
+
ThreadPool.QueueUserWorkItem(DownloadTiles);
}
}
}
+
private void DownloadTiles(object o)
{
Tile tile;
while (pendingTiles.TryDequeue(out tile))
{
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
- byte[] imageBuffer = DownloadImage(tile);
+ byte[] buffer = DownloadImage(tile.Uri);
- if (imageBuffer != null && CreateTileImage(tile, imageBuffer) && Cache != null)
+ if (buffer != null && CreateTileImage(tile, buffer) && Cache != null)
{
- Cache.Set(CacheKey(tile), imageBuffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
+ Cache.Set(CacheKey(tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
}
}
+
+ Interlocked.Decrement(ref downloadThreadCount);
}
private string CacheKey(Tile tile)
@@ -157,18 +161,39 @@ namespace MapControl
return string.Format("{0}/{1}/{2}/{3}", tileLayer.Name, tile.ZoomLevel, tile.XIndex, tile.Y);
}
- private byte[] DownloadImage(Tile tile)
+ private bool CreateTileImage(Tile tile, byte[] buffer)
+ {
+ BitmapImage bitmap = new BitmapImage();
+
+ try
+ {
+ using (Stream stream = new MemoryStream(buffer, 8, buffer.Length - 8, false))
+ {
+ bitmap.BeginInit();
+ bitmap.CacheOption = BitmapCacheOption.OnLoad;
+ bitmap.StreamSource = stream;
+ bitmap.EndInit();
+ bitmap.Freeze();
+ }
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
+ return false;
+ }
+
+ tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = bitmap));
+ return true;
+ }
+
+ private static byte[] DownloadImage(Uri uri)
{
- HttpWebRequest request = null;
byte[] buffer = null;
try
{
- TraceInformation("{0} - Requesting", tile.Uri);
-
- request = (HttpWebRequest)WebRequest.Create(tile.Uri);
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.UserAgent = typeof(TileImageLoader).ToString();
- request.KeepAlive = true;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
@@ -185,67 +210,36 @@ namespace MapControl
}
}
- TraceInformation("{0} - Completed", tile.Uri);
+ Trace.TraceInformation("Downloaded {0}", uri);
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
- TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)ex.Response).StatusCode);
+ HttpStatusCode statusCode = ((HttpWebResponse)ex.Response).StatusCode;
+ if (statusCode != HttpStatusCode.NotFound)
+ {
+ Trace.TraceInformation("Downloading {0} failed: {1}", uri, ex.Message);
+ }
}
else
{
- TraceWarning("{0} - {1}", tile.Uri, ex.Status);
+ Trace.TraceWarning("Downloading {0} failed with {1}: {2}", uri, ex.Status, ex.Message);
}
}
catch (Exception ex)
{
- TraceWarning("{0} - {1}", tile.Uri, ex.Message);
+ Trace.TraceWarning("Downloading {0} failed: {1}", uri, ex.Message);
}
return buffer;
}
- private bool IsCacheOutdated(byte[] imageBuffer)
+ private static bool IsCacheOutdated(byte[] buffer)
{
- long creationTime = BitConverter.ToInt64(imageBuffer, 0);
+ long creationTime = BitConverter.ToInt64(buffer, 0);
return DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow;
}
-
- private bool CreateTileImage(Tile tile, byte[] imageBuffer)
- {
- BitmapImage bitmap = new BitmapImage();
-
- try
- {
- using (Stream stream = new MemoryStream(imageBuffer, 8, imageBuffer.Length - 8, false))
- {
- bitmap.BeginInit();
- bitmap.CacheOption = BitmapCacheOption.OnLoad;
- bitmap.StreamSource = stream;
- bitmap.EndInit();
- bitmap.Freeze();
- }
- }
- catch (Exception ex)
- {
- TraceWarning("Creating tile image failed: {0}", ex.Message);
- return false;
- }
-
- Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = bitmap));
- return true;
- }
-
- private static void TraceWarning(string format, params object[] args)
- {
- Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
- }
-
- private static void TraceInformation(string format, params object[] args)
- {
- //Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
- }
}
}
diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs
index 6fa1c99b..b2ebc1e4 100644
--- a/MapControl/TileLayer.cs
+++ b/MapControl/TileLayer.cs
@@ -133,7 +133,8 @@ namespace MapControl
drawingContext.DrawRectangle(tile.Brush, null, tileRect);
//if (tile.ZoomLevel == zoomLevel)
- // drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y), System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
+ // drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y),
+ // System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
});
}
}