Bind button click inside customlayout using mvvmcross and mvxlistview - android

i wanted to bind a button click event inside my customlayout, below is my customlayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="63dp">
<LinearLayout
android:orientation="horizontal"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:text="Accept"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/acceptBtnOnList"
android:background="#color/green_color"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="bold"
android:layout_weight="1"
android:textColor="#android:color/background_light"
local:MvxBind="Click AcceptCommand" />
</LinearLayout>
</RelativeLayout>
below is my ListView layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/background_light">
<Mvx.MvxListView
android:id="#+id/ListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#FFDAFF7F"
local:MvxBind="ItemsSource MyList; ItemClick ShowDetailCommand"
local:MvxItemTemplate="#layout/customlayout" />
</LinearLayout>
as you can see above i have called my customlayout inside the listview layout
Below is my ViewModelClass
public class ListViewModel : BaseViewModel
{
public IListService ListService { get; set; }
private MvxCommand _acceptCommand;
private ListAcceptedResult _accepted;
private MvxCommand _detailsCommand;
private ObservableCollection<MyCustomClass> _myList = new ObservableCollection<MyCustomClass>();
public ListViewModel(IListService listService)
{
ListService = listService;
}
public ObservableCollection<MyCustomClass> MyList
{
get { return _myList; }
set
{
_myList = value;
RaisePropertyChanged(() => MyList);
}
}
public ListAcceptedResult Accepted
{
get { return _accepted; }
set
{
_accepted = value;
RaisePropertyChanged(() => Accepted);
Update();
}
}
public ICommand AcceptCommand
{
get
{
IsLoading = true;
return
new MvxCommand<MyCustomClass>(
item =>
//On Success assigning the returned value from service to Accepted Property,
error => { IsLoading = false; ReportError(error.Message); }));
}
}
public async System.Threading.Tasks.Task Update()
{
//update logic
}
}
But i am not able to bind the AcceptCommand command to my button.
i am aware that this will not work because inside my customlayout view i do not get the AcceptCommand command as it is not a part of object MyCustomClass
please help me with some example.
Thanks in advance

As you know, data binding works by binding directly to the current ViewModel/Model. You're not able to access properties or commands of the parent view model, unless you provide a property to access the parent.
Anyway, one option is to create a value converter that you use in your binding. This converter will return a MvxCommand object that when executed will use MvxMessenger to publish a message.
The parent view model will subscribe to this message and then execute the command that you want.
I've created a sample based on Stuart Lodge's N=02 example.
https://github.com/kiliman/MvxCommandToMessage
EDIT: I modified the sample to use a generic MessageToCommandValueConverter. You can now pass in the message type in the binding. You still need specific message types though since MvxMessenger.Publish() is global to your app. See the code on GitHub for the changes.
Here's the value converter:
public class KittenAcceptedMessageValueConverter : MvxValueConverter<Kitten, ICommand>
{
protected override ICommand Convert(Kitten kitten, Type targetType, object parameter, CultureInfo culture)
{
return new MvxCommand(() =>
{
var messenger = Mvx.Resolve<IMvxMessenger>();
var message = new KittenAcceptedMessage(this, kitten);
messenger.Publish(message);
});
}
}
And here's how you bind to it in your layout. Use . to pass the current object to the converter.
<Mvx.MvxImageView
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_margin="10dp"
local:MvxBind="ImageUrl ImageUrl; Click KittenAcceptedMessage(.)" />
And then finally in your ViewModel, you would subscribe to this message, and call your command:
_messenger.Subscribe<KittenAcceptedMessage>(message =>
{
KittenAcceptedCommand.Execute(message.Kitten);
});
private MvxCommand<Kitten> _kittenAcceptedCommand;
public ICommand KittenAcceptedCommand
{
get
{
_kittenAcceptedCommand = _kittenAcceptedCommand ?? new MvxCommand<Kitten>(kitten =>
{
var toast = Mvx.Resolve<IToastPlugin>();
toast.Show(string.Format("You accepted {0}", kitten.Name));
});
return _kittenAcceptedCommand;
}
}
Hope this helps.

Related

How to create a recyclerview using xamarin android mvvmcross?

I have followed the example from https://github.com/xamarin/monodroid-samples/tree/master/android5.0/RecyclerViewSample/RecyclerViewSample for displaying the data in recyclerview in xamarin android. It is working fine for me.
But as per our project requirement, i need to display a recyclerview using xamarin android mvvmcross. I have searched for examples but could not find a working one.
Code:
main.xml
<MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView
android:id="#+id/recylerView"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginLeft="#dimen/margin_normal"
android:layout_marginRight="#dimen/margin_normal"
android:layout_marginBottom="#dimen/margin_normal"
android:layout_marginTop="#dimen/margin_normal"
android:background="#FFFF00"
android:orientation="horizontal"
android:divider="#null"
android:dividerHeight="#dimen/divider_height_medium"
local:MvxItemTemplate="#layout/item_list"
local:MvxBind="ItemsSource CoffeeList" />
Fragment.cs
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.Inflate(Resource.Layout.main, null);
return v;
}
item_list.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text FirstName" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text LastName" />
</LinearLayout>
ViewModel.cs
public class CoffeeViewModel : MvxViewModel
{
public CoffeeViewModel ()
{
}
private string _firstName;
public string FirstName
{
get => _firstName;
set => SetProperty(ref _firstName, value);
}
private string _lastName;
public string LastName
{
get => _lastName;
set => SetProperty(ref _lastName, value);
}
}
Now when i run the application, the recyclerview is displaying empty. Can you please let me know if I am missing anything while binding the data.
First of all install the package MvvmCross.Droid.Support.V7.RecyclerView.
Here you have an example of a simple MvxRecyclerView:
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
...
local:MvxItemTemplate="#layout/item_template"
local:MvxBind="ItemsSource Items; ItemClick ItemClickCommand; ItemLongClick ItemLongClickCommand" />
So Items is your ObservableCollection<T> of your ViewModel, ItemClickCommand is the command of your ViewModel that will be executed when you click on a row and ItemLongClick is the command of your ViewModel that will be executed when you do a long click on a row.
Let's say you have a public ObservableCollection<PersonItemViewModel> Items {get;set;}
And your PersonItemViewModel has the next properties:
private string _firstName;
public string FirstName
{
get => _firstName;
set => SetProperty(ref _firstName, value);
}
private string _lastName;
public string LastName
{
get => _lastName;
set => SetProperty(ref _lastName, value);
}
So your item_template.axml that will be renderized as each row of the MvxRecyclerView. It's DataContext (to which you do the bindings) is each of the items of the ObservableCollection<PersonItemViewModel>, i.e. a PersonItemViewModel. Here an example of the layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text FirstName" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text LastName" />
</LinearLayout>
More info in Android MvxRecyclerView
HIH
If you are using Mvvmcross 6. Put that code in your Setup.cs at your .Droid project:
protected override IEnumerable<Assembly> AndroidViewAssemblies =>
new List<Assembly>(base.AndroidViewAssemblies)
{
typeof(MvxRecyclerView).Assembly,
};
Now you don't have to use the full path of MvxRecyclerView (MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView).
Now you can use the MvxRecyclerView just like de RecyclerView from Android.

How to display an image with MvxImageView into a MvxListView

I have a MvxListView in Xamarin.Android app, and an ItemTemplate to display a name and a picture retrieve from WS. If the name is displayed, it is not the case of the picture ...
View_Main.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00007f"
android:textColor="#ffffff"
android:textSize="24dp"
android:layout_margin="30dp"
android:padding="20dp"
android:layout_marginTop="10dp">
<Mvx.MvxListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource ItemsModels"
local:MvxItemTemplate="#layout/item_list" />
</LinearLayout>
item_list.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="22dp"
local:MvxBind="Text Name" />
<Mvx.MvxImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
local:MvxBind="ImageUrl Image" />
</LinearLayout>
I have the internet permission in AndroidManifest.xml, and I installed DownloadCache and File plugins.
Urls are good, I display them in Visual Studio Console and when I click on them, image display.
I followed this video to try to display image, but I don't know why it works for him and not for me ...
https://www.youtube.com/watch?v=JBzj_nkeLFI
Maybe because he uses MvvmCross 3.0 and I'm using MvvmCross 4.0 ...
FIRST EDIT
My ItemModel class
public class ItemModel
{
public string Name { get; set; }
public string Image { get; set; }
public string Text { get; set; }
public bool IsFavorite { get; set; }
}
and a typical value of Image property : http://inu.tapptic.com/test/image.php?text=%E5%8D%81%E5%85%AB
The problem is not the URL, I tried with a URL found on google image, and it doesn't work too
SECOND EDIT
public class MainViewModel : MvxViewModel
{
private readonly IDataProviderService _dataProviderService;
#region Fields
private ObservableCollection<ItemModel> _itemsModels;
#endregion
#region Properties
public ObservableCollection<ItemModel> ItemsModels
{
get { return _itemsModels; }
set
{
_itemsModels = value;
RaisePropertyChanged(() => ItemsModels);
}
}
#endregion
#region Constructors
public MainViewModel(IDataProviderService dataProviderService)
{
_dataProviderService = dataProviderService;
}
#endregion
public override void Start()
{
TimeWatcher.BeforeExecution();
Task.Run(() => LoadData()); // A voir car ça lance un autre "faux thread", mais ça bloque pas l'UI
base.Start();
TimeWatcher.ExecutionEnd();
}
private async void LoadData()
{
ItemsModels = await _dataProviderService.GetItemsAsync("http://dev.tapptic.com/", "test/json.php");
// Just to see if URLs are good
foreach(var itemsModel in ItemsModels)
Debug.WriteLine(itemsModel.Image);
}
}

Visibility Converters in Xamarin Android

I am creating a android application using MvvmCross, in which I have to show and hide some controls in listview depending upon the value. For that I have created a visibility converter in PCL like this
public class VisibilityValueConverter : MvxValueConverter<bool, MvxVisibility>
{
protected override MvxVisibility Convert(bool value, Type targetType, object parameter, CultureInfo culture)
{
return (value == true) ? MvxVisibility.Visible : MvxVisibility.Collapsed;
}
}
and I am using this value converter in my layout page like this
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
local:MvxBind="Text QuestionText"
android:layout_marginTop="15dp" />
<RadioGroup
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/radioGroup1"
android:layout_marginTop="5dp">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/radioButton1"
local:MvxBind="Text OptionA" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text OptionB"
android:id="#+id/radioButton2" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text OptionC"
android:id="#+id/radioButton3" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text OptionD"
android:id="#+id/radioButton4" />
</RadioGroup>
<EditText
android:layout_width="fill_parent"
android:layout_height="159.0dp"
android:textSize="20dp"
android:layout_marginTop="2dp"
local:MvxBind="Visibility TexboxVisible,Converter=Visibility" />
</LinearLayout>
But it's not working. It's not hitting the breakpoints in PCL value converter.
I have also tried to use MvxVisibility plugin but it's also not working.
I think I am doing something wrong. Can someone help and let me know how to use visibilty converters inside listview in android.
ViewModel
public class Question
{
public string Type { get; set; }
public bool RadioVisible { get; set; }
public bool TexboxVisible { get; set; }
}
private List<Question> _questionList;
public List<Question> QuestionList
{
get { return _questionList; }
set
{
_questionList = value;
RaisePropertyChanged(() => QuestionList);
}
}
private async void ShowQuestionsList(int assignmentId)
{
QuestionList = await _service.GetQuestionListByAssignmentAsync(assignmentId);
if (QuestionList != null)
{
foreach (Question q in QuestionList)
{
if (q.Type != null)
{
if (q.Type == "S")
{
q.RadioVisible = false;
q.TexboxVisible = true;
}
else if (q.Type == "O")
{
q.RadioVisible = true;
q.TexboxVisible = false;
}
}
}
}
}
My breakpoint in my Testconverter is fired as it should. My code:
public class TestMethodValueConverter : MvxValueConverter<bool, MvxVisibility>
{
protected override MvxVisibility Convert(bool value, Type targetType, object parameter, CultureInfo culture)
{
return value ? MvxVisibility.Visible : MvxVisibility.Collapsed;
}
}
And the View-Xaml:
local:MvxBind="Visibility MyBoolProperty, Converter=TestMethod"
But there is another problem. The android view elements can't change the visibility with the MvxVisibility enum. They need a Android.Vioews.ViewStates value.
So you need to add the converter in the Android project. Thats why we use the MvxVisibility-Plugin.
Edit
Your viewmodels should all inherit from MvxViewModel and the properties, which are used for binding need to implement the property-changed call RaisePropertyChanged(() => Property);. Otherwise nobody knows about changes. Thats the first point.
But the Converter should work at the first time without that (as far as I know). So I don't see anything other which can go wrong.. so try to create a simple clean project only with that problem and one single View-Element to reproduce what can go wrong..

Mvvm cross binding data context

I am trying to implement a mvvm cross solution. I am facing an issue with bindings..
I am trying to implement the solution in xamarin.android.
Below is my Main Layout page - Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
local:MvxBind="Text Title/>
<Mvx.MvxListView
android:id="#+id/SRMTypeList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:fastScrollEnabled="true"
local:MvxItemTemplate="#layout/list_Item"
local:MvxBind="ItemsSource PersonCollection" />
</LinearLayout>
Below is my View Model:
public class MainViewModel :MvxViewModel
{
private string title;
public String Title
{
get{ return title;}
set
{
title = value;
RaisePropertyChanged (()=> Title);
}
}
List<Person> _personCollection;
List<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
RaisePropertyChanged (() => PersonCollection);
}
public MainViewModel()
{
_personCollection = new List<Person>();
PersonCollection.Add(new Person{Name="Steve", Salary=10000});
PersonCollection.Add(new Person{Name="Mary", Salary=20000});
}
}
MainView.cs
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
}
The issue starts here in the item template for my list view in the main screen list_Item.axml is shown below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<CheckBox
android:id="#+id/checkbox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="?" />
<TextView
android:textColor="#fff"
android:textSize="16sp"
local:MvxBind="?"/>
</LinearLayout>
How to make the binding for TextView's Text and CheckBox to parent's view model (ie. in the Main View Model class)?.
Any pointer/ help to solve this will be highly appreciated.
I have gone through below links .. but being a newbie was not able to understand the implementation.
Binding button click in ListView template MvvMCross
MVVMCross changing ViewModel within a MvxBindableListView
I am not able to understand how to create the wrapper class and how to set the data context of the item template (list_Item.axml) to this wrapper class.
Is their any way in mvvm cross so that i can refer bindings in the item template directly to the parent view model in my case which is MainViewModel.
Can anyone kindly post a simpler example?
Thanks
I'm not sure if that's what you are asking, but to make a binding of the person's name to the listitem textview:
local:MvxBind="Text Name"
And instead of using a List you should use an ObservableCollection
ObservableCollection<Person> _personCollection;
ObservableCollection<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
RaisePropertyChanged (() => PersonCollection);
}
}
For the checkbox i would add a field to the Person class such as IsSelected and bind it to the checkbox:
local:MvxBind="Checked IsSelected"

MvvmCross - manually add View to Linear Layout - access model inside event

I have a page which is a ScrollView. I am going to put some TextViews and some ListViews in it. Because people said: cannot use ListView inside ScrollView, so I use LinearLayout instead.
This is .axml:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="Text Item.Info1"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/linearLayout1" />
</ScrollView>
This is ViewModel:
public class ItemDetailViewModel : MvxViewModel{
...
private Item _item;
public Item Item {
get { return this._item; }
set {
_item= value;
RaisePropertyChanged(() => Item );
}
}
....
In View, I manually add TextView to linearLayout1. I want to access subItem when click TextView. But I do not know how to do this. Could you please look into below comments and help? Thanks.
protected override void OnCreate(Bundle bundle) {
ItemDetailViewModel vm = this.ViewModel as ItemDetailViewModel ;
RunOnUiThread(() =>
{
LinearLayout linearLayout1 = FindViewById<LinearLayout>(Resource.Id.linearLayout1);
foreach (SubItem subItem vm.Item.SubItems)
{
//I am stuck here, don't know how to access subItem in Click event??
TextView textView = new TextView(this);
textView.Text = subItem.itemName;
textView.Click += textView_Click;
linearLayout1.AddView(textView);
//I know there is another way to do this by using existed axml but ...
/*
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="Text ItemName; Click DoSomethingWithItem" />
*/
TextView textView = (TextView)LayoutInflater.Inflate(Resource.Layout.Sub_Item, null);
var set = this.CreateBindingSet<.., ItemDetailViewModel >();
set.Bind(textView ).To(vm => vm....); //How can I access subItem from vm??
set.Apply();
}
});
});
....
private void adEquipmentTextView_Click(object sender, System.EventArgs e)
{
//TODO:do something with subItem
}
You could just create an anonymous wrapper for each method?
i.e. instead of calling:
textView.Click += textView_Click;
call:
textView.Click += (s,e) => DoClickOn(subItem);

Categories

Resources