Xamarin FOrms routing with passing parameters in Shell - xamarin

In my codeBehind of my Page1 I have:
{
//do not select item
((ListView)sender).SelectedItem = null;
//go to stockDetailsPage
await Shell.Current.GoToAsync("postlogin/stockdetails?tickerSymbol=AAPL.NASDAQ");
}
As you can see I hardcoded the value in order to test it.
Earlier it was without param, and it work, now I need to pass param tickerSymbol.
And in my stockDetailsPage in my ViewModel I added
[QueryProperty("TickerSymbol", "tickerSymbol")] above my class
and in my class I declared properties:
public string TickerSymbol
{
set { SetProperty(ref tickerSymbol, Uri.UnescapeDataString(value)); }
get { return tickerSymbol; }
}
So in my constructor of that vm, I now want to call method with input argument of that param, but Im getting null all the time.
Can you suggest me where Im wrong?

As Xamarin doc said it should be able to pass param to the class for the page's BindingContext, I tried the following and it works:
in code behind, set BindingContext to your ViewModel:
TestParamViewModel testParamViewModel;
public TestPage()
{
InitializeComponent();
testParamViewModel = new TestParamViewModel();
this.BindingContext = testParamViewModel;
}
Add QueryPropertyAttribute to your ViewModel:
[QueryProperty("Name", "name")]
class TestParamViewModel
{
string name;
public string Name
{
set
{
name = Uri.UnescapeDataString(value);
}
}
}
call navigation:
await Shell.Current.GoToAsync("testpage?name=Abyssinian");
Hope it helps.

Related

What purpose does [QueryProperty("Name", "name")] serve in a Xamarin Shell app?

Looking at the Xamarin shell samples I see this:
namespace Xaminals.Views
{
[QueryProperty("Name", "name")]
public partial class CatDetailPage : ContentPage
{
public string Name
{
set
{
BindingContext = CatData.Cats.FirstOrDefault(m => m.Name == Uri.UnescapeDataString(value));
}
}
public CatDetailPage()
{
InitializeComponent();
}
protected override bool OnBackButtonPressed()
{
return base.OnBackButtonPressed();
}
}
}
Does anyone know the purpose of: [QueryProperty("Name", "name")]
It allows you to pass parameters to a page when using url navigation
It is discussed here
For pages to receive data when navigating, the page class must be decorated with a QueryPropertyAttribute.
When you set
[QueryProperty("Name", "name")]
The first argument - "Name" refers to the name of the property that will be receiving the data.
The second argument - "name" refers to the parameter id that will be passed in the query
You can read more in the official documentation

How to notify change in ItemSource for repeater view Xamarin forms

I'm trying to implement ItemSource in StackLayout in Xamarin..Fforms using RepeaterView. Example here.
Now whenever there is a change in ObservableCollection in ViewModel, RepeaterView Class is not able to notify the change as there is no INotifyCollectionChanged property. How to add that property to the RepeaterView class?
Here's my code:
private ObservableCollection<string> _userName = new ObservableCollection<string>();
public ObservableCollection<string> UserName
{
get
{
return _userName;
}
set
{
SetProperty(ref _userName, value);
}
}
On button click I'm adding strings to the List:
UserName.Add("User_1");
UserName.Add("User_2");
UserName.Add("User_3");
UserName.Add("User_4");
You could try something like this:
var names = new List<string>();
names.Add("User1");
names.Add("User2");
names.Add("User3");
UserName = new ObservableCollection(names);

Set different binding context for custom user control

I am trying to create my first Xamarin.Forms custom user control named LocationSelector. It has an Entry and when the user enters something, a list with selectable locations is shown (similary to the selection in Google Maps).
The selected location is the important 'return value' of the control.
My plan is to catch the ItemSelected event of the list and set the SelectedLocation property. LocationSelector is designed as MVVM and since everything is working so far here just the Code-Behind (which I think is enough to describe the problem):
public partial class LocationSelector : StackLayout
{
public static readonly BindableProperty SelectedLocationProperty =
BindableProperty.Create<LocationSelector, LocationModel>(s => s.SelectedLocation, new LocationModel(), BindingMode.TwoWay);
public LocationSelector()
{
InitializeComponent();
var model = new LocationSelectorModel();
BindingContext = model;
_listView.ItemSelected += (sender, args) =>
{
SelectedLocation = model.SelectedLocation;
};
}
public LocationModel SelectedLocation
{
get { return (LocationModel)GetValue(SelectedLocationProperty); }
set { SetValue(SelectedLocationProperty, value); }
}
}
Now I want to use this control on a search view where the BindingContext is set to the SearchViewModel:
<ContentPage x:Class="Application.App.Views.SearchView" ...>
<c:LocationSelector SelectedLocation="{Binding Location}"/>
</ContentPage>
public class SearchViewModel : ViewModel
{
private LocationModel _location;
public LocationModel Location
{
get { return _location; }
set { SetProperty(ref _location, value); }
}
}
Unfortunately this is not working. The output throws a binding warning:
Binding: 'Location' property not found on 'Application.App.CustomControls.LocationSelectorModel', target property: 'Application.App.CustomControls.LocationSelector.SelectedLocation'
Why points the binding to a property in the ViewModel that is used 'within' my custom control and not to the property in the BindingContext of the view?
The problem is setting the BindingContext to the view model of the user control:
public LocationSelector()
{
var model = new LocationSelectorModel();
BindingContext = model; // this causes the problem
// ...
}
In this post I found the solution. Setting the BindingContext to each child elements rather than the whole user control is doing the job:
public LocationSelector()
{
var model = new LocationSelectorModel();
foreach (var child in Children)
{
child.BindingContext = model;
}
}

Receive Notifications from Child ViewModels

I might be missing something simple, so bear with me.
I have a ViewModel that contains the following:
public ObservableCollection<Person> PersonCollection
{
get { return personCollection; }
set
{
if (personCollection != value)
{
personCollection = value;
RaisePropertyChanged("PersonCollection");
}
}
}
Then in another ViewModel I have:
public ObservableCollection<Person> PersonCollection
{
get
{
PersonViewModel vm = (App.Current.Resources["Locator"] as ViewModelLocator).PersonViewModel;
return vm.PersonCollection;
}
}
public PersonViewModel PersonViewModel
{
get
{
return ((App.Current.Resources["Locator"] as ViewModelLocator).PersonViewModel)
}
}
In my XAML if I bind to PersonCollection then updates don't happen on my view, but if I bind to PersonViewModel.PersonCollection it does. so is this the "proper" way to do it or is there a way for the view to detect the notifications using the first approach?
Change your binding to {Binding PersonViewModel.PersonCollection}
Your wrapped PersonCollection property has no change notifications, so the view doesn't know that the property has changed (it certainly has no way of knowing it originally came from PersonViewModel in order to get change notifications from it)

MVC3 shared-search model confusion

(couldn't think of a better title, sorry)
So I've got my layout page, on this page there is a searchbar + options. Choosing whatever, should take you through to the search page, with the results etc. Fairly standard. What I've done to get this working is to create a MasterModel class, with a SearchDataModel class member on it. This SearchDataModel contains the various parameters for the search (search term, what fields to search on etc).
I've then strongly typed my layout page to the MasterModel class, and using a Html.BeginForm... I've constructed the search form for it. However all the checkboxes relating to the fields aren't checked by default, even though the default value for all the fields is true (via a private getter/setter setup).
Yet when I submit the form to the SearchController, all the checkboxes are set to true. So I'm a bit confused as to why it knows they should be true, yet not set the checkboxes to be checked?
Putting breakpoints in key places seems to show that the model isn't insantiated on the get requests, only the post to the Search controller?
I may be going about this all wrong, so if so, pointers as to the right way always appreciated.
public class MasterModel {
public SearchDataModel SearchModel { get; set; }
}
public class SearchDataModel{
private bool _OnTags = true;
private bool _OnManufacturers = true;
private bool _OnCountries = true;
[Display(Name= "Tags")]
public bool OnTags {
get { return _OnTags; }
set { _OnTags = value; }
}
[Display(Name= "Manufacturers")]
public bool OnManufacturers {
get { return _OnManufacturers; }
set { _OnManufacturers = value; }
}
[Display(Name= "Countries")]
public bool OnCountries {
get { return _OnCountries; }
set { _OnCountries = value; }
}
[Required]
[Display(Name="Search Term:")]
public string SearchTerm { get; set; }
}
Then in the _layout page:
#Html.CheckBoxFor(m => m.SearchModel.OnTags, new { #class="ddlCheckbox", #id="inpCheckboxTag" })
#Html.LabelFor(m =>m.SearchModel.OnTags)
Make sure you return a MasterModel with initialized SearchModel from your views:
public ActionResult Index()
{
var model = new MasterModel
{
SearchModel = new SearchDataModel()
};
return View(model);
}
Another possibility to implement this functionality than strongly typing your master layout to a view model is yo use Html.Action as shown by Phil Haack in his blog post.

Resources