I'm trying to change MapView in my Xamarin Forms app (with Mapsui and Prism), as I need separate view to store Pins. First MapView (let's call it default) is for displaying all pins from list. Second MapView (history) is for displaying new pins, which are removed when leaving Page.
I have Dictionary for storing my MapViews to have easy way to access any view I want. Every created MapView has the same instance of Map. I want to change from default to history which should hide pins that are added in default view.
I change view with this:
public void ChangeView(string name)
{
var prev = MapView;
loggerService.Info($"Pins: {prev.Pins.Count}");
MapView = GetView(name); // Current MapView in XAML, returns instance of MapView
loggerService.Info($"New view pins: {MapView.Pins.Count}");
Task.Factory.StartNew(() =>
{
Task.Delay(2000).Wait();
loggerService.Info($"View pins: {MapView.Pins.Count}");
});
MapView.Refresh();
}
extra code with log is for debugging purposes - it shows correct values (prev pins = 7, new pins = 0, view pins = 0).
I've added history view without Zoom buttons to make sure it is changing - and it is (so there is no need to post XAML, I think).
How should I change MapView to hide pins when navigating to history and show them when I switch back to default view?
Or is there a better way to 'group' Pins and hide/show them basing on name?
Update
I think this may be important to mention:
public Pin AddMarker(Position point)
{
var pin = new Pin(MapView)
{
Label = "PinType.Pin",
Position = point,
Type = PinType.Pin,
Transparency = 0.5f,
Color = Xamarin.Forms.Color.FromRgb(2, 144, 210),
Scale = 0.5f,
};
MapView.Pins.Add(pin);
return pin;
}
This is the way I add Pins to view. This is the same MapView as above (all the code is from MapService).
Some options
Perhaps you can bind the IsVisible property of the Pins to a HistoryMode field on your view. I never used this myself.
Use some of the lower level functionality. Like
Use separate layers for default and history and enable/disable them when needed. You might need to do the conversion of WGS84 (gps coordinates) to SphericalMercator. https://mapsui.com/api/Mapsui.Projection.SphericalMercator.html?q=sphericalmercator
Use a ThemeStyle. It has a method that can be use to specify any style you need (visible/color/symbol) based and the feature attributes. https://mapsui.com/api/Mapsui.Styles.Thematics.ThemeStyle.html?q=themestyle
For the lower level functionality you may need to the samples to get you on your way: https://mapsui.com/documentation/samples.html
Related
im using Xamarin with MvvmCross.
Ive done a FragmentDialog with a recyclerView inside, the list is populated via bindings on xml file, so i have no adapter and i should keep it this way.
If im not wrong, theres no built in way to make the recyclerView take only the size needed for its content, this should not be a problem, but in this case i need the list to start from bottom...
So i did this (its a custom fullscreen dialog) :
MvxRecyclerView list = Dialog.FindViewById<MvxRecyclerView>(Resource.Id.recyclerview);
list.LayoutChange += List_LayoutChange;
Then in layoutChange
private void List_LayoutChange(object sender, View.LayoutChangeEventArgs e)
{
MvxRecyclerView list = Dialog.FindViewById<MvxRecyclerView>(Resource.Id.recyclerview);
int itemHeight = list.GetChildAt(0).Height;
if (itemHeight != 0)
{
ViewGroup.LayoutParams prms = list.LayoutParameters;
prms.Height = itemHeight * list.GetAdapter().ItemCount;
list.LayoutParameters = prms;
list.LayoutChange -= List_LayoutChange;
list.RequestLayout();
}
}
That was working fine, the list get exactly the height needed and the list looks like it starts from bottom.
Now the client tell me that he doesnt like the fullscreen dialog and wants the status bar, i think that should be easy, just to remove this line at the dialog creation right?
dialog.Window.AddFlags(WindowManagerFlags.Fullscreen);
But looks like its not that easy, when the dialog its not fullscreen the layoutParams change seems to have no effect, it just dont do nothing.
My method is being called and i get the right item height, it just dont change the recyclerview height.
Notice that setting fullscreen at creation and clearing the flag after the recyclerview params change works
So looks like it only works during fullscreen mode.
Can someone throw some light at this?
Thanks in advance.
As you said, RecyclerView was not aware of its size.
Since last update to the support lib, it is !
http://android-developers.blogspot.fr/2016/02/android-support-library-232.html
The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.
I would suggest to wait for the Xamarin wrapped lib (there is already a beta https://www.nuget.org/packages/Xamarin.Android.Support.v4/23.2.0-beta1)
I have pushpins on my map in Visual Studio (below is the code that does this). But they are blue dots. I want to change my pushpins to something else besides blue dots. I found this website that gives a bunch of syntax of pushpins. (http://msdn.microsoft.com/en-us/library/ff701719.aspx) Currently, it displays the pushpin as number 77. I don't know if this is the default pushpin, or what. But I want to change it to something else like 71 so it gives more information. Any idea how I can do this with my code I have below?
Thanks!! :)
private async void PopulateMap()
{
//put data on map
LocationCollection locationCollection = new LocationCollection();
if (_group != null)
{
foreach (SampleDataItem item in _group.Items)
{
Pushpin myPushpin = new Pushpin();
myPushpin.Text = item.Title;
//created new location
Location rentLocation = new Location(Convert.ToDouble(item.Latitude), Convert.ToDouble(item.Longitude));
MapLayer.SetPosition(myPushpin, rentLocation);
rentalMap.Children.Add(myPushpin);
locationCollection.Add(rentLocation);
}
}
The documentation you referenced is for the Static imagery API and those pushpins are not available outside of that service. That said it's very easy to create custom pushpins. If you simply want to change the color try using the background property of the Pushpin class. Alternatively you can create a custom style that has a DataTemplate and apply that to the pushpin. If you simply want to use an image as a pushpin you can create an Image object, or any UIElement and add it to the map the same way you do a pushpin.
I'm assuming you are creating a Windows 8 app. If so, take a look at my free ebook on crating location intelligent windows store apps. In chapter 4 I show how to create custom pushpins: http://rbrundritt.wordpress.com/my-book/
You can also find all the code samples for the book here: http://code.msdn.microsoft.com/Location-Intelligence-for-1c691d0e
I'm using a custom list adapter for a list view. I have defined a button in the list view and the click event works, but the problem is that once the list is scrolled, it binds multiple views with the same button. So on the click of the button, the event associated with each of the associated views is fired.
How do I deal with this?
I would guess that you are misunderstanding how the list works - especially how convertView's are used.
ListViews in Android virtualise the UI - just like ListBoxes do in WP and just like UITableViews do in iOS
What this means is that if the underlying list has 1000 items, but the screen only has room for 10 items, then the list will just create 10 'containers' to show list items, and will use those containers to display just the content that is in view at the time.
The way it does this is through the Adapter - and in particular through the GetView callback - which takes a convertView as one of its parameters.
If you choose to create a new view in your GetView implementation, then you can subscribe to new events in the callback...
If instead you choose to use the convertView in your GetView implementation, then you should not subscribe to new events in the callback - not without unsubscribing the old events first.
e.g. I'm guessing your code does something like this pseudo currently:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
}
toShow.Text = "Item at position " + i;
toShow.Click += (s,e) => {
// do something
};
return toShow;
}
The problem with the code is that you will subscribe to Click too often... you'd need to solve it with something like:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
toShow.Click += (s,e) => {
// do something with the position embedded in toShow.Tag
};
}
toShow.Text = "Item at position " + i;
toShow.Tag = new WrappedPosition(i);
return toShow;
}
That's my guess anyways :)
Stuart is completely right - problem is that views in ListView are reused (to avoid creating different objects), and as different parts of list are visible, for a new position you could get any view that is not used anymore. So your code should handle this properly.
I would like to add that Garbage Collection for Java objects in monodroid works not good. In my experience, creating lots of objects derived from Java.Lang.Object will crash the application. So:
Creating new View for each new row will soon crash the applicaion, so you have to reuse convertView whenever possible.
Tag has type Java.Lang.Object, so WrappedPosition should derive from Java object. This means that rather than creating new instance every time, you should reuse same instance.
If you move click handler to a separate method, you can just unsubscribe before subscribing, so you will not need any logic "if view is null".
If you find it useful, I may post here example of code that explains how it works. Dodn't post it initially as it's quite big :)
In my Silverlight project I am creating textboxes which are two-way databound to some Context during runtime. The binding in one direction (from the source to the target) seems to work fine, but the other direction (from the target back to the source) is not showing any effect.
This is the data-context:
public class Leg : INotifyPropertyChanged {
private string passengers;
public string Passengers {
get { return passengers; }
set {
// here I have a breakpoint.
passengers = value;
FirePropertyChanged("Passengers");
}
}
private void FirePropertyChanged (string property) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then on another place I am creating a new TextBox control together with a binding for it:
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
Now when I am altering the value of the Passengers string the corresponding textbox that is bound to it is updating its text correctly. So here's everthing fine.
But when i change the text of a textbox manually and then make the textbox lose its focus, nothing happens - i.e. there is no two-way binding taking place - no down propagation of the new text-value of the textbox to the source !
I have a breakpoint at the setter of the passengers-attribute (marked with the breakpoint-comment above). When I am getting all this right the binding engine also uses this public setter when the target-value of a binding has changed to update the source - so when this happens the breakpoint must be hit. But he doesn't ! So it seems that i can do what I want with my textbox (play with the focus or press enter) it is never updating its source.
Am I overseeing something ? There must be a capital error either in my code or in my thinking.. i would be really thankful for any ideas ...
EDIT:
In the following I try to demonstrate how i create my XAML objects and my DataContext objects. Because I am creating XAML controls and their bindings at runtime I haven't found a good solution to implement the MVVM approach very well. So I am doing the following (which is maybe not the best way to do it):
The situation I am modelling is that I have a UserControl (called LegItem) which is comprised (primarely) of textboxes. At runtime the user can create as much of these userControls as hew wishes to (one after the other).
On my ViewModel side I have a class (called Leg) that serves as a ViewModel for exactly one LegItem. So when I have say n (XAML-) LegItems then I also have n Leg instances. I store these Leg objects in a List.
So I am doing the following everytime the user clicks the 'add a new leg' button:
// here we are inside the applications view in an .xaml.cs file
public void AddLeg () {
// this is going to serve as the ViewModel for the new LegItem
// I am about to create.
Leg leg = viewModel.insertLeg();
// here I am starting to create the visual LegItem. The ViewModel object
// I have created in the previous step is getting along with.
createLegItem(leg);
}
// the primary job here is to bind each contained textbox to its DataContext.
private LegItem createLeg (Leg viewModelLeg) {
// create the visual leg item control element
// which is defined as a XAML UserControl.
LegItem legItem = new LegItem();
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
}
// on the viewModel side there is this simple method that creates one Leg object
// for each LegItem the View is creating and stores inside a simple list.
public Leg InsertLeg () {
Leg leg = new Leg();
legList.add(leg)
return leg;
}
New Answer
Since you mentioned your binding was actually to a custom UserControl and not actually a TextBox, I would suggest looking into the XAML of your UserControl and making sure it is binding the data correctly
Old Answer
I did a quick test with a new Silverlight project and noticed that the startup project is SilverlightApplication1.Web, not SilverlightApplication.
This means that the breakpoint in the setter won't actually get hit when I run the project. You'll notice the breakpoint circle is just the outline, and the color isn't filled in. If you hover over it, it will say
The breakpoint will not currently be hit. No symbols have been loaded
for this document
If I start SilverlightApplication1 instead of the .Web version, the breakpoint gets hit.
The property is getting changed correctly regardless of which version I startup, however the breakpoint isn't getting hit if I start the project with the .Web version. I suspect this is your issue.
I'm trying to change the color of a UITableViewController when using a split view. Then the style is set to grouped, the background seems to light gray no matter what.
I create a new iPad Split Application solution and add the following code to RootViewController:
public RootViewController () : base( UITableViewStyle.Grouped) // base ("RootViewController", null)
{
this.TableView.BackgroundColor = UIColor.Red;
this.View.BackgroundColor = UIColor.Red;
this.View.Layer.BorderColor = UIColor.Green.CGColor;
this.TableView.BackgroundView.BackgroundColor = UIColor.Brown;
I'm pulling my hair out trying to work out why its always light gray.
You do not change the color of the controller but of one (or many) of its views.
Also it's generally TintColor that must be used (not BackgroundColor). A bit confusing (I think it's related to the use of gradients, over the background, used in many place - but I could be wrong).