How to notify change in ItemSource for repeater view Xamarin forms - xamarin

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);

Related

Xamarin FOrms routing with passing parameters in Shell

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.

Xamarin: set labelText with an instance of ViewModel isnt updating UI

I defined a Label in Xaml
<Label Text="{Binding DeviceGuid}"/>
set the BindingContext inside my Page
BindingContext = new BluetoothViewModel();
and wrote the code for the getter and setter in the ViewModel
private string _deviceGuid;
public string DeviceGuid
{
get
{
return _deviceGuid;
}
set
{
if (_deviceGuid != value)
{
_deviceGuid = value;
OnPropertyChanged();
}
}
}
So thats the simple Thing :). The Binding works if I change the value inside the ViewModel.
Now here it comes:
There are some Backgroundtasks (or just other classes) that, in my opinion, should have Access to that property and if they will write it, the UI should update automatically.
I think its bad practice but I dont know how to realise it different.
I´ve already tried to create another instance of the viewmodel like
BluetoothViewModel a = new BluetoothViewModel();
a.DeviceGuid = "test";
Its calling the OnPropertyChanged() but isnt updating the UI ...
Thanks for your help in advance.
When you do this:
BluetoothViewModel a = new BluetoothViewModel();
a.DeviceGuid = "test";
You are creating another instance of the viewmodel that is not the one in your BindingContext.
Do this instead:
public BluetoothViewModel viewmodel;
BindingContext = viewmodel= new BluetoothViewModel();
And then:
viewmodel.DeviceGuid = "test";
The reason it must be happening is that you are not making these changes in the MainThread which is the thread responsible for making changes on the UI.
Do something like below where you change the property data:
Device.BeginInvokeOnMainThread(() => {
DeviceGuid="New string"; });
Update
What you should be doing is using the BindingContext and creating a new instance so your variable 'a' should look something like below
private BluetoothViewModel viewmodel;
BindingContext = viewmodel= new BluetoothViewModel ();
And then do this
viewmodel.DeviceGuid="New string";

Xamarin Native, Binding actions to listview items

I would like to ask about bindings. What is the best approach to bind some actions in listview items in ios and android using xamarin in mvvm world. As I understand, we have few approaches.
1.
For every list item we have some Model, and to this model we have to add some Commands.
For example:
public class ItemModel
{
public string MyName { get; set; }
public ICommand RemoveCommand { get; set; }
}
Where in ViewModel we have SomeInitMethod
public class ViewModel
{
public ObservableCollection<ItemModel> Items {get;set;}
public async Task SomeInitMethod
{
Items = new ObservableCollection(await _myApiService.FetchItemsAsync());
foreach(var item in Items)
{
item.Command = new RelayCommand(RemoveItem);
}
}
public void RemoveItem(ItemModel item)
{
Items.Remove(item);
}
}
But I see a drawback in SomeInitMethod where we should set RemoveCommand. What if we should to set 2 or even more commands than we duplicate code in ListItemView(somehow we need to bind all these commands)?
Next approach is somehow handle events of remove/toggle buttons and others in Listview and then delegate this commands directly to ViewModel.
Example:
ContactsListView.ItemRemoveClicked += (ItemModel model) => ViewModel.RemoveItem
Advantages is: we no longer need to handle commands in ViewModel
Drawback is: we need every time to write custom ListView and support event handling in code-behind.
The last approach is to send ViewModel to ListItem to set Commands.
Example
somewhere we have method CreateListViewItem on the view, let's say on iOS.
private void InitTableView() {
TableView.RegisterNibForCellReuse(ItemViewCell.Nib, ItemViewCell.Key);
var source = new ObservableTableViewSource <ItemModel>
{
DataSource = ViewModel.Items,
BindCellDelegate = (cell, viewModel, index) =>
{
if (cell is ItemModel memberCell)
{
memberCell.BindViewModel(viewModel);
memberCell.RemoveItem = (item) => ViewModel.RemoveItem;
}
}
};
TableView.Source = source;
}
Advantages: we no longer need to have Commands in Model, and we don't need to setup this Commands in ViewModel.
Possibly, drawback is that we somehow need to have ViewModel reference.
In WPF or UWP you have DataContext, you can binding directly to ViewModel.
Which approach you use, maybe I miss something, and it would be perfect if you provide some examples or thoughts.
Thanks.

How do you bind asynchronously to a Kendo UI control?

I've got a drop-down selection box that has 2,000 items in it. I tried to bind it like this:
#(Html.Kendo().MultiSelect().Name("kendo-dropdown-manager")
.DataValueField("Value")
.DataTextField("Text")
.Placeholder("Select Entity")
.BindTo(Model.ManagersList)
In my Razor Page, I implement the 'Get' asynchronously:
public class IndexModel : PageModel
{
public IndexModel()
{
this.ManagersList = new List<SelectListItem>();
}
public List<SelectListItem> ManagersList { get; private set; }
public Task OnGetAsync()
{
ViewsController viewsController = new ViewsController();
List<ManagerViewModel> managers = await viewsController.GetManagersAsync();
this.ManagersList.AddRange(
from m in managers
select new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
}
}
When the page appears, the drop down is empty. If I change 'OnGetAsync' to a completely synchronous version, the drop down is populated correctly. My page has several similar controls and I don't want to load them up synchronously. What's the proper way to start an operation and have the Kendo controls catch the results when they're returned?

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;
}
}

Resources