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

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

Related

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.

Dynamic ViewModel navigation

I am trying to find a way to be able to set from the View to what ViewModel I have to navigate. This is to be able to change the navigation flow without changing the core project.
I thought the easier way would be creating an interface, setting the target ViewModel there and injecting the interface into the ViewModel to then perform the navigation.
public interface IModelMapping
{
MvxViewModel ViewModelToNavigate();
}
public class MyViewModel : MvxViewModel
{
readonly IMvxNavigationService navigationService;
readonly IModelMapping modelMapping;
public MyViewModel(IMvxNavigationService navigationService, IModelMapping modelMapping)
{
this.navigationService = navigationService;
this.modelMapping = modelMapping;
}
public IMvxAsyncCommand GoContent
{
get
{
IMvxViewModel vm = modelMapping.ViewModelToNavigate();
IMvxAsyncCommand navigateCommand = new MvxAsyncCommand(() => navigationService.Navigate<vm>());
return navigteCommand;
}
}
}
The problem with this code is I am getting an error setting the navigationService.Navigate(). The error is 'vm is a variable but it is used like a type'
What about using the URI navigation together with the facade? See also https://www.mvvmcross.com/documentation/fundamentals/navigation#uri-navigation
Say you are building a task app and depending on the type of task you want to show a different view. This is where NavigationFacades come in handy (there is only so much regular expressions can do for you).
mvx://task/?id=00 <– this task is done, show read-only view (ViewModelA)
mvx://task/?id=01 <– this task isn’t, go straight to edit view (ViewModelB)
[assembly: MvxRouting(typeof(SimpleNavigationFacade), #"mvx://task/\?id=(?<id>[A-Z0-9]{32})$")]
namespace *.NavigationFacades
{
public class SimpleNavigationFacade
: IMvxNavigationFacade
{
public Task<MvxViewModelRequest> BuildViewModelRequest(string url,
IDictionary<string, string> currentParameters, MvxRequestedBy requestedBy)
{
// you can load data from a database etc.
// try not to do a lot of work here, as the user is waiting for the UI to do something ;)
var viewModelType = currentParameters["id"] == Guid.Empty.ToString("N") ? typeof(ViewModelA) : typeof(ViewModelB);
return Task.FromResult(new MvxViewModelRequest(viewModelType, new MvxBundle(), null, requestedBy));
}
}
}

Prism - moving data between viewmodels

I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.

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 selected option of type model in dropdownlist with knockout

I'm trying to load the saved option from server side with knockout, please see below the general idea,
I have the following classes in javascript:
funcion Request() {
this.Id = ko.observable('');
this.Name = ko.observable('');
this.Form = ko.obsevable('');
}
function Form() {
this.Id = ko.observable('');
this.Name = ko.observable('');
}
this is my viewModel
function RequestViewModel() {
var self = this;
self.Request = new Request();
*self.Request.Form = new Form();*
}
I can save the Form without problem, but when I try to load the Form field saved into the database, the binding doesn't function.
If anybody have ever had the same problem, please let me know How can I fix it?
You form is an observable. When setting an observable, you should call it as a method, and set parse the value as a parameter. Something like this:
function RequestViewModel() {
var self = this;
self.Request = new Request();
self.Request.Form(new Form());
}
If you have loaded the form from the database, it should look something like this:
self.Request.Form(myLoadedForm);
The answer by aaberg is correct, but if you're saying you need to load them all at once, I recommend you use the knockout mapping plugin to automate this: http://knockoutjs.com/documentation/plugins-mapping.html
Your call would look something like this:
ViewModel = ko.mapping.fromJS(requestFromServer);

Resources