Windows 8 Secondary Tile using Templates - c#

I have a windows store app that currently allows my users to pin and unpin tiles to the start menu. I am able to set a background and logo and text but the problem is I want the tile to instead of a static logo with text I want it to be sort of a live tile.
So it would go from one side with just an image and then flip and the other side would be my app information. I know you are able to do this for regular live tiles and using xml, but I am using c# and would like for this to work in my secondary tile.
Any help on how to go about that would be great.
Here is some code I used to create the secondary tile:
private async void PinButton_OnClick (object sender, RoutedEventArgs e)
{
var item = createdItem.SelectedItem;
if (item != null)
{
var logo = new Uri(item.image);
if (item != null)
{
var smallLogo = new Uri(item.image);
var wideLogo = new Uri(item.image);
}
string tileActivationArguments = logoSecondaryTileId + "WasPinnedAt=" +
DateTime.Now.ToLocalTime();
logoSecondaryTileId = item.ID+ counter.ToString();
counter++;
//Create the tile
SecondaryTile tile = new SecondaryTile(logoSecondaryTileId, item.Code, item.FirstName + " " + item.LastName,
tileActivationArguments, TileOptions.ShowNameOnLogo | TileOptions.ShowNameOnWideLogo, logo);
if (item.SelectedItem is Details)
{
tile.ForegroundText = ForegroundText.Light;
tile.BackgroundColor = Colors.Black;
bool isPinned =
await
tile.RequestCreateForSelectionAsync(GetElementRect((FrameworkElement)sender),
Placement.Default);
if (isPinned)
{
Messages.InvokeToast(Toast.Created);
UnpinButton.Visibility = Visibility.Visible;
pinButton.Visibility = Visibility.Collapsed;
}
else
{
Messages.InvokeToast(Toast.Error);
}
if (SecondaryTile.Exists(logoSecondaryTileId))
{
var dialog = new MessageDialog("Already exists!")
{
Title = "Unable to Pin Tile!"
};
dialog.Commands.Add(new UICommand("Okay", new UICommandInvokedHandler(CommandHandler)));
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
}
}

You just need to define the tile using a template that has a front and back. See here for how to define secondary tiles with templates: Is it possible to use tile templates with secondary tiles in a Windows Store App?. See here for the list of possible templates and which have backs: http://msdn.microsoft.com/en-us/library/windows/apps/hh761491.aspx

Related

How to make Win2D BlendEffect apply to current drawing surface (background)?

I want to draw some images on to existing canvas using multiply blend mode. However, I don't know how to do it as the BlendEffect class require me to assign the Background variable but that is suppose to be the canvas which I could not put there.
private void OnDrawCanvas(CanvasControl sender, CanvasDrawEventArgs args)
{
var list = new LinkedList<ImageNode>();
mRootNode.GetTraverseList(list, false);
foreach (var item in list)
{
if (!item.treeVisible)
continue;
if (item.mLayerPixels != null)
{
if (item.mLayer.BlendModeKey == BlendModeType.MULTIPLY)
{
var blendEffect = new BlendEffect()
{
//Background = ???, // what to put????
Foreground = item.mLayerPixels,
Mode = BlendEffectMode.Multiply
};
args.DrawingSession.DrawImage(blendEffect, item.mLayer.Left, item.mLayer.Top);
}
else
{
args.DrawingSession.DrawImage(item.mLayerPixels, item.mLayer.Left, item.mLayer.Top);
}
}
}
}
I ended up creating an offscreen CanvasRenderTarget to do the blending. When all the drawing is done, I create a CanvasBitmap from CanvasRenderTarget which allow me to draw the final result to the UI with args.DrawingSession.DrawImage();

Unable to locate cause of memory leak in Android app

I built out this whole app thinking that the garbage collector handled memory clean-up just fine, which was incredibly stupid and naive of me, but hey, it was my first time every using Xamarin to build an app, and my first time ever building an app, so what's a guy to do? Every screen seems to leak memory, but the screens that leak the most are screens that have bitmaps, generating a memory dump and analyzing it in MAT, I found the following:
So there are 4 potential culprits, 2 are bitmaps, 2 are byte arrays. This is a heap dump for the main menu of the app, if I go into my list view activity for listing out elements, I get 5 potential leaks from bitmaps. Here is the code for the activity:
AssetManager assets = Assets;
Window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
var topPanel = FindViewById<TextView>(Resource.Id.topPanel);
topPanel.Text = service.GetLanguageValue("use recommendations - top bar heading");
topPanel.Dispose();
var lowerPanel = FindViewById<TextView>(Resource.Id.recommendationsPanel);
lowerPanel.Text = service.GetLanguageValue("title upper - recommendations by variety");
Shared.ScaleTextToOneLine(lowerPanel, lowerPanel.Text, Shared.ScaleFloatToDensityPixels(Shared.GetViewportWidthInDp()), 1.0f);
lowerPanel.Dispose();
// Read html file and replace it's contents with apple data
string html = "";
using (StreamReader sr = new StreamReader(Assets.Open("apple-variety-detail.html")))
{
html = sr.ReadToEnd();
}
html = ReplaceAppleDetailsHtml(html);
var webview = FindViewById<WebView>(Resource.Id.recommendationsMessage);
CleanWebView();
webview.LoadDataWithBaseURL("file:///android_asset/",
html,
"text/html", "UTF-8", null);
if (Shared.currentApple != null)
{
// Setup apple image
using (var imageView = FindViewById<ImageView>(Resource.Id.recommendationsImage))
{
var apple = this.apples.Where(a => a.Id == Shared.currentApple.AppleId).Select(a => a).First();
var imgName = apple.Identifier.First().ToString().ToUpper() + apple.Identifier.Substring(1);
var fullImageName = "SF_" + imgName;
using (var bitmap = Shared.decodeSampledBitmapFromResource(ApplicationContext.Resources,
Resources.GetIdentifier(fullImageName.ToLower(), "drawable", PackageName),
200, 200))
{
imageView.SetImageBitmap(bitmap);
}
}
// Setup apple name
FindViewById<TextView>(Resource.Id.appleNameTextView).Text = Shared.currentApple.Name;
}
else
{
FindViewById<TextView>(Resource.Id.appleNameTextView).Text = "Not Found!";
}
// Setup list menu for apples
AppleListView = FindViewById<ListView>(Resource.Id.ApplesListMenu);
// Scale details and list to fit on the same screen if the screen size permits
if (Shared.GetViewportWidthInDp() >= Shared.minPhoneLandscapeWidth)
{
var listViewParams = AppleListView.LayoutParameters;
// Scales list view to a set width
listViewParams.Width = Shared.ScaleFloatToDensityPixels(240);
listViewParams.Height = Shared.ScaleFloatToDensityPixels(Shared.GetViewportHeightInDp());
AppleListView.LayoutParameters = listViewParams;
}
else
{
// Here, we either need to hide the list view if an apple was selected,
// or set it to be 100% of the screen if it wasn't selected.
if(!Shared.appleSelected)
{
var listViewParams = AppleListView.LayoutParameters;
// Scales list view to a set width
listViewParams.Width = Shared.ScaleFloatToDensityPixels(Shared.GetViewportWidthInDp());
listViewParams.Height = Shared.ScaleFloatToDensityPixels(Shared.GetViewportHeightInDp());
AppleListView.LayoutParameters = listViewParams;
}
else
{
var listViewParams = AppleListView.LayoutParameters;
// Scales list view to a set width
listViewParams.Width = Shared.ScaleFloatToDensityPixels(0);
listViewParams.Height = Shared.ScaleFloatToDensityPixels(Shared.GetViewportHeightInDp());
AppleListView.LayoutParameters = listViewParams;
}
}
// Set listview adapter
if(AppleListView.Adapter == null)
{
AppleListView.Adapter = new Adapters.AppleListAdapter(this, (List<Apple>)apples, this);
}
AppleListView.FastScrollEnabled = true;
// Set the currently active view for the slide menu
var frag = (SlideMenuFragment)FragmentManager.FindFragmentById<SlideMenuFragment>(Resource.Id.SlideMenuFragment);
frag.SetSelectedLink(FindViewById<TextView>(Resource.Id.SlideMenuRecommendations));
// Replace fonts for entire view
Typeface tf = Typeface.CreateFromAsset(assets, "fonts/MuseoSansRounded-300.otf");
FontCrawler fc = new FontCrawler(tf);
fc.replaceFonts((ViewGroup)this.FindViewById(Android.Resource.Id.recommendationsRootLayout));
tf.Dispose();
}
The important part to note about this is the way this activity works is it loads an adapter, and when it displays it shows a list of items, when an item is clicked, it reloads this same activity, and it computes the screen size, shrinks down the list to show only the webview off to the side, and displays details about the item, thus simulating 2 screens, the reason I did this is because when the screen size is larger, it needs to show all of this as one single view, so on larger screens it will actually show both the listview and the webview, but still reload the activity to load new data.
The adapter code is probably what is giving me a hard time, but I'm not sure, I've tried quite a few things, but nothing seems to help, here's the adapter code:
public class AppleListAdapter : BaseAdapter<Apple>
{
List<Apple> items;
Activity context;
ApplicationService service = AgroFreshApp.Current.ApplicationService;
private Context appContext;
private Typeface tf;
static AppleRowViewHolder holder = null;
public AppleListAdapter(Activity context, List<Apple> items, Context appContext): base ()
{
this.context = context;
this.items = items;
this.appContext = appContext;
context.FindViewById<ListView>(Resource.Id.ApplesListMenu).ChoiceMode = ChoiceMode.Single;
tf = Typeface.CreateFromAsset(context.Assets, "fonts/MuseoSansRounded-300.otf");
}
public override long GetItemId(int position)
{
return position;
}
public override Apple this[int position]
{
get { return items[position]; }
}
public override int Count
{
get
{
return items.Count;
}
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = convertView;
var imgName = item.Identifier.First().ToString().ToUpper() + item.Identifier.Substring(1);
var fullImageName = "SF_" + imgName;
if (view == null)
{
view = context.LayoutInflater.Inflate(Resource.Layout.appleRowView, null);
}
if (view != null)
{
holder = view.Tag as AppleRowViewHolder;
}
if(holder == null)
{
holder = new AppleRowViewHolder();
view = context.LayoutInflater.Inflate(Resource.Layout.appleRowView, null);
holder.AppleImage = view.FindViewById<ImageView>(Resource.Id.iconImageView);
holder.AppleName = view.FindViewById<TextView>(Resource.Id.nameTextView);
view.Tag = holder;
}
using (var bitmap = Shared.decodeSampledBitmapFromResource(context.Resources,
context.Resources.GetIdentifier(fullImageName.ToLower(), "drawable", context.PackageName),
25, 25))
{
holder.AppleImage.SetImageBitmap(bitmap);
}
holder.AppleName.Text = AgroFreshApp.Current.AppleDetailManager.GetAll().Where(a => a.AppleId == item.Id).Select(a => a.Name).FirstOrDefault();
holder.AppleName.SetTypeface(tf, TypefaceStyle.Normal);
view.Click += (object sender, EventArgs e) =>
{
var apple = AgroFreshApp.Current.AppleManager.Get(item.Id);
Shared.currentApple = AgroFreshApp.Current.AppleDetailManager.GetAll().Where(a=>a.AppleId == item.Id && a.LanguageId == service.UserSettings.LanguageId).Select(a=>a).FirstOrDefault();
Shared.appleSelected = true;
Intent intent = new Intent(appContext, typeof(RecommendationsActivity));
intent.SetFlags(flags: ActivityFlags.NoHistory | ActivityFlags.NewTask);
appContext.StartActivity(intent);
};
return view;
}
}
So I'm using the viewholder pattern here, and assigning click events to each list item as they get generated, with nohistory and newtask as the intent flags so that the pages refreshes properly. To clean up the bitmaps, I have been using these two methods:
This cleans the large image on the details webview:
public void CleanBitmap()
{
// Clean recommendations bitmap
ImageView imageView = (ImageView)FindViewById(Resource.Id.recommendationsImage);
Drawable drawable = imageView.Drawable;
if (drawable is BitmapDrawable)
{
BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;
if (bitmapDrawable.Bitmap != null)
{
Bitmap bitmap = bitmapDrawable.Bitmap;
if (!bitmap.IsRecycled)
{
imageView.SetImageBitmap(null);
bitmap.Recycle();
bitmap = null;
}
}
}
Java.Lang.JavaSystem.Gc();
}
And this cleans the bitmaps stored in each listview item:
public void CleanListViewBitmaps()
{
var parent = FindViewById<ListView>(Resource.Id.ApplesListMenu);
// Clean listview bitmaps
for (int i = 0; i < parent.ChildCount; i++)
{
var tempView = parent.GetChildAt(i);
// If the tag is null, this no longer holds a reference to the view, so
// just leave it.
if(tempView.Tag != null)
{
AppleRowViewHolder tempHolder = (AppleRowViewHolder)tempView.Tag;
var imageView = tempHolder.AppleImage;
var drawable = imageView.Drawable;
if (drawable is BitmapDrawable)
{
BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;
if (bitmapDrawable.Bitmap != null)
{
Bitmap bitmap = bitmapDrawable.Bitmap;
if (!bitmap.IsRecycled)
{
imageView.SetImageBitmap(null);
bitmap.Recycle();
bitmap = null;
}
}
}
}
}
Java.Lang.JavaSystem.Gc();
}
They then get called in the activities ondestroy method like so:
protected override void OnDestroy()
{
base.OnDestroy();
CleanBitmap();
CleanListViewBitmaps();
Shared.appleSelected = false;
}
I'm also using a shared class with static variables to essentially track view states like if something was selected or no, but it only stores primitives, it doesn't store any view objects or anything like that, so I don't think that is the problem like I said it looks like bitmaps aren't getting cleaned correctly, and it seems to happen on every view, but this one in particular is bad.
I also on each view load 2 fragments, one is a slide menu fragment in a frame layout, and the other is a navbar fragment that just holds 2 bitmaps for a logo and menu handle, so those could be culprits too I suppose. Here's the navbar fragment:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
var view = inflater.Inflate(Resource.Layout.navbar, container, false);
var navLogo = view.FindViewById(Resource.Id.navbarLogo);
var menuHandle = view.FindViewById(Resource.Id.menuHandle);
var navSpacer = view.FindViewById(Resource.Id.navSpacer);
((ImageButton)(menuHandle)).SetMaxWidth(Shared.GenerateProportionalWidth(.25f, 50));
((ImageButton)(menuHandle)).SetMaxHeight(Shared.GenerateProportionalHeight(.25f, 50));
((ImageButton)(menuHandle)).Click += (object sender, EventArgs e) =>
{
var slideMenu = FragmentManager.FindFragmentById(Resource.Id.SlideMenuFragment);
if (slideMenu.IsHidden)
{
FragmentManager.BeginTransaction().Show(slideMenu).Commit();
}
else if (!slideMenu.IsHidden)
{
FragmentManager.BeginTransaction().Hide(slideMenu).Commit();
}
};
var navLogoParams = navLogo.LayoutParameters;
// Account for the padding offset of the handle to center logo truly in the center of the screen
navLogoParams.Width = global::Android.Content.Res.Resources.System.DisplayMetrics.WidthPixels - (((ImageButton)(menuHandle)).MaxWidth * 2);
navLogoParams.Height = (Shared.GenerateProportionalHeight(.25f, 30));
navLogo.LayoutParameters = navLogoParams;
// Spacer puts the logo in the middle of the screen, by making it's size the same as the handle on the opposite side to force-center the logo
((Button)(navSpacer)).SetMaxWidth(Shared.GenerateProportionalWidth(.25f, 50));
((Button)(navSpacer)).SetMaxHeight(Shared.GenerateProportionalHeight(.25f, 50));
return view;
}
Does anyone see any obvious or stupid mistake that I'm making? I feel like it has to just be sheer inexperience that's causing me to miss something really obvious, or I'm doing something completely wrong, either way.
EDIT #1:
1 of the bitmaps leaking was the menu handle button in the navigation fragment, so that drops the leak down from 300kb to 200kb, but I still need to figure out how to clean it properly.
EDIT #2:
Here is my code that scales bitmaps down
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight)
{
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeResource(res, resId, options);
// Calculate inSampleSize
options.InSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
return BitmapFactory.DecodeResource(res, resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
int halfHeight = height / 2;
int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth)
{
inSampleSize *= 2;
}
}
return inSampleSize;
}
For anyone wondering, I've figured out the problem. Xamarin is a c# wrapper around native java, so at runtime there is the native Java runtime, and the mono runtime as well, so any object like a bitmap that you want to cleanup, you need to cleanup the native Java object, but you also need to clean up the c# handle to the native object, because what happens is the garbage collector goes to see if it should clean your resource, sees a handle associated with the resource, and moves on. My solution was to call the c# dispose after I cleaned up the native Java object, and then call both the c# and Java garbage collector, I'm not sure if calling both garbage collectors is explicitly needed, but I chose to do it anyway. Seriously hope this helps someone out, I do not envy people who have to hunt down these problems.
Sometimes Bitmaps ar not garbage collected correctly, and generete the outofmemory exception.
my suggestion if you're working with bitmaps is to call
System.gc();
to recycle bitmaps from memory correctly

WIA: setting dinamic page size

I'm developing a WPF application to scan different documents with a scanner. The size of the documents won't be the same, can be variable.
I have my code working without scanner dialogs, and I would like the user not to have to preview the image and then scanning it to get the real size (resulting in two scans).
The problem is that I try to set page-size to auto before scanning
SetWIAProperty(item.Properties, "3097", 100);
but I get HRESULT: 0x80210067 System.Runtime.InteropServices.COMException.
I've googled to this and seens that my scanner is not supporting this property.
So, is there any way of achieving this? I need the resulting scanned image to be only the document, not all the scanner area (which I'm obtaning right now).
In case I couldn't tell the scanner to scan only the document, I've thought also in cropping the resulting image to obtain only the document I need, but don't know how to do this right now.
Here is my code:
DeviceManager deviceManager = new DeviceManager();
Device scanner = null;
foreach (DeviceInfo deviceInfo in deviceManager.DeviceInfos)
{
if (deviceInfo.DeviceID == scannerId)
{
scanner = deviceInfo.Connect();
break;
}
}
if (scanner == null)
{
throw new Exception("Scanner not found");
}
Item item = scanner.Items[1] as Item;
int dpi = 300;
SetWIAProperty(item.Properties, "6146", 1); // 1 Color
SetWIAProperty(item.Properties, "6147", dpi); // dpis
SetWIAProperty(item.Properties, "6148", dpi); // dpis
// This line throws the exception
//SetWIAProperty(item.Properties, "3097", 100); // page size 0=A4, 1=letter, 2=custom, 100=auto
try
{
ICommonDialog wiaCommonDialog = new CommonDialog();
ImageFile scannedImage = (ImageFile)wiaCommonDialog.ShowTransfer(item, FormatID.wiaFormatPNG, false);
if (scannedImage != null)
{
ImageProcess imgProcess = new ImageProcess();
object convertFilter = "Convert";
string convertFilterID = imgProcess.FilterInfos.get_Item(ref convertFilter).FilterID;
imgProcess.Filters.Add(convertFilterID, 0);
SetWIAProperty(imgProcess.Filters[imgProcess.Filters.Count].Properties, "FormatID", FormatID.wiaFormatPNG);
scannedImage = imgProcess.Apply(scannedImage);
if (System.IO.File.Exists(#"D:\temp\scanwia3.png"))
System.IO.File.Delete(#"D:\temp\scanwia3.png");
scannedImage.SaveFile(#"D:\temp\scanwia3.png");
}
scannedImage = null;
}
finally
{
item = null;
scanner = null;
}
And SetWIAProperty function:
private static void SetWIAProperty(IProperties properties, object propName, object propValue)
{
Property prop = properties.get_Item(ref propName);
prop.set_Value(ref propValue);
}
Any help would be appreciated.
Kind regards,
Jose.
Property Page Size belongs to device, not to item.
var WIA_IPS_PAGE_SIZE = "3097";
var WIA_PAGE_AUTO = 100;
SetWIAProperty(scanner.Properties, WIA_IPS_PAGE_SIZE, WIA_PAGE_AUTO);

C# WP8.1 - Displaying a MapIcon on top of a Route when using Bing Maps

I am just playing around with bing maps at the moment. I have followed the tutorials to create routes and add mapicons etc, however I have found that a mapIcon wont show if it on the route. I have tried playing with the Z-Index property of the mapIcon, however this seemed to have little effect, as I think it only has an effect with other MapElements.
Does anyone else know a way to make this happen?
My current code for creating the route and trying to set a MapIcon on the destination is (For abit of Content MapFunctions is just a static class I've made for functons such as finding the route, get the current location etc):
private async void DrawRoute()
{
// Check if a destination has been set
if(MapFunctions.destination != null)
{
route = await MapFunctions.GetRouteAsync(MapFunctions.destination.Point);
if (route != null)
{
// Use the route to initialize a MapRouteView.
MapRouteView viewOfRoute = new MapRouteView(route.Route);
viewOfRoute.RouteColor = Colors.Yellow;
viewOfRoute.OutlineColor = Colors.Black;
// Add the new MapRouteView to the Routes collection
// of the MapControl.
MyMap.Routes.Add(viewOfRoute);
// Fit the MapControl to the route.
await MyMap.TrySetViewBoundsAsync(
route.Route.BoundingBox,
null,
Windows.UI.Xaml.Controls.Maps.MapAnimationKind.None);
AddDestinationMapElement(MapFunctions.destination.Point);
// Start timer to update the remaining time of the journey
UpdateRemainingTime_Tick(this, new Object());
dispatcherTimer.Start();
}
else
{
MessageDialog message = new MessageDialog("An error occured while trying to calculate your route. Please try again later.", "Error");
await message.ShowAsync();
}
}
}
private void AddDestinationMapElement(Geopoint dest)
{
MapIcon MapIcon1 = new MapIcon();
MapIcon1.Location = new Geopoint(new BasicGeoposition()
{
Latitude = dest.Position.Latitude,
Longitude = dest.Position.Longitude
});
MapIcon1.Visible = true;
MapIcon1.ZIndex = int.MaxValue;
MapIcon1.Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Images/mapIcon.png"));
MapIcon1.NormalizedAnchorPoint = new Point(0.5, 1.0);
MapIcon1.Title = "Destination";
MyMap.MapElements.Add(MapIcon1);
}
If you want to add pushpin on top of your elements, you can use MapOverlay that will allow you to add pushpin (with image or any XAML element) into your map control:
MapOverlay pushpinStart = new MapOverlay();
pushpinStart.PositionOrigin = new Point(0.5, 0.5);
pushpinStart.Content = new Image()
{
Source =
new BitmapImage(
new Uri("../Assets/Ui/ui-pin-start.png", UriKind.Relative))
};
pushpinStart.GeoCoordinate = posCollection[0];
If you want to stay with MapIcon, then the rendering engine will calculate what's best to be displayed based on the zoom level and current location as well as other elements collision algorithm's result. So I'm not sure that what you're looking for.
And for WP8.1, here is the equivalent code:
Image iconStart = new Image();
iconStart.Source = new BitmapImage(new Uri("ms-appx:///Assets/Ui/ui-pin-start.png"));
this.Map.Children.Add(iconStart);
MapControl.SetLocation(iconStart, new Geopoint(pos[0]));
MapControl.SetNormalizedAnchorPoint(iconStart, new Point(0.5, 0.5));

Bing Maps foreach item in list add new pushpin

I'm trying to add multiple pushpins from a list to Bing Maps on Windows Phone. The name of each pushpin needs to be different, because I want to be able to remove them individually later using MainMap.Children.Remove(SpecificPushpin);.
This is my foreach:
Pushpin pushpin = new Pushpin();
Attractions attractions = new Attractions();
foreach (var attraction in Attractions.allAttractions)
{
pushpin.GeoCoordinate = new GeoCoordinate(attraction.Latitude, attraction.Longtitude);
pushpin.Content = attraction.Title;
pushpin.Background = new SolidColorBrush(Colors.Blue);
pushpin.Foreground = new SolidColorBrush(Colors.White);
MainMap.Children.Add(pushpin);
}
Of course, I receive an error after the first loop through the foreach on the MainMap.Children.Add(pushpin); line, because "pushpin" is already an existing name.
I also tried using this:
MainMap.Children.Add(new Pushpin() { Content = attraction.Title, GeoCoordinate = new GeoCoordinate(attraction.Latitude, attraction.Longtitude), Background = new SolidColorBrush(Colors.Yellow), Foreground = new SolidColorBrush(Colors.Black) });
But then I will never be able to romove the pushpins individualy.
Does anyone know how I can give a variable name to each pushpin in my list, or knows another way to fix my problem?
Instead of using Bing Maps, I used the Map Control and worked with MapLayers and MapOverlay. I created a different MapLayer for each list class and removed those individually.
This made it possible to remove one list of pushpins simply. Here's an example:
private void LoadAttractions()
{
if (cbxAttractions.IsChecked != false)
{
Attractions attractions = new Attractions();
foreach (var attraction in Attractions.allAttractions)
{
Pushpin pushpin = new Pushpin();
pushpin.Name = attraction.Title;
pushpin.GeoCoordinate = new GeoCoordinate(attraction.Latitude, attraction.Longtitude);
pushpin.Content = attraction.Title;
pushpin.Background = new SolidColorBrush(Colors.Yellow);
pushpin.Foreground = new SolidColorBrush(Colors.Black);
MapOverlay MyOverlay = new MapOverlay();
mapLayerAttractions.Add(MyOverlay);
MyOverlay.Content = pushpin;
MyOverlay.GeoCoordinate = new GeoCoordinate(attraction.Latitude, attraction.Longtitude);
MyOverlay.PositionOrigin = new Point(0.0, 1.0);
}
MainMap.Layers.Add(mapLayerAttractions);
}
}
And when cbxAttractions is being tapped:
private void cbxAttractions_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
if (cbxAttractions.IsChecked == false)
{
MainMap.Layers.Remove(mapLayerAttractions);
}
else
{
LoadAttractions();
}
}
You can give each your Pushpin a property, for example:Tag. And Each tag is not same to another. So when you remove a specific pushpin. You can use foreach like this:
foreach(PushPin pushpin in yourPushpinList)
{
if (pushpin.Tag == yourValue)
{
MainMap.Children.Remove(SpecificPushpin);
break;
}
}
the code maybe not correct, it is just a example.

Categories