I want to display a list of Youtube Video using ListView and WebView. But Videos are not loaded.
WebView always shows An Error Occured as the picture below:
Currently I don't know how to bind URL directly to WebView in AXML file, therefor I customized a BindableWebView instead:
public class BindableWebView : WebView
{
public BindableWebView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
private string _webViewContent;
public string WebViewContent
{
get { return _webViewContent; }
set
{
_webViewContent = value;
LoadHtmlString();
}
}
private void LoadHtmlString()
{
LoadData(WebViewContent, "text/html", "utf-8");
}
}
This is Layout AXML file:
<dc.AlphaLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="Click ItemClickCommand">
<!--Video Thumbnail-->
<FrameLayout
android:layout_width="#dimen/ProductImageWidth"
android:layout_height="#dimen/ProductImageHeight"
android:layout_margin="#dimen/ExtraNewsImagePadding">
<BindableWebView
android:id="#+id/playerWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="WebViewContent Video.YoutubeUrl" />
</FrameLayout>
<LinearLayout
.....
</LinearLayout>
</dc.AlphaLinearLayout>
This is Video Model:
namespace ....Shared.Models
{
public class Video
{
public string YoutubeId { get; set; }
public string YoutubeImageUrl => $"https://img.youtube.com/vi/{YoutubeId}/0.jpg";
public string YoutubeUrl => $"<iframe width=\"100%\" height=\"100%\" src=\"https://www.youtube.com/embed/{YoutubeId}\" frameborder=\"0\" allowfullscreen></iframe>";
public Guid NewsId { get; set; }
}
}
And VideoListViewm in C#:
namespace DeHeus.Droid.Views
{
public class VideoListView : DetailViewWithShare
{
protected override int LayoutId => Resource.Layout.VideoListView;
private CustomMvxListView _videoListview;
protected override void InitView(View view)
{
_videoListview = view.FindViewById<CustomMvxListView>(Resource.Id.videoList);
_videoListview.ItemTemplateId = Resource.Layout.VideoListItemView;
}
protected override void CreateBinding()
{
var bindingSet = this.CreateBindingSet<VideoListView, VideoListViewModel>();
bindingSet.Bind(_videoListview.Adapter)
.For(v => v.ItemsSource)
.To(vm => vm.VideoItemViewModels);
bindingSet.Bind(_videoListview)
.For(v => v.ScrollToBottom)
.To(vm => vm.ScrollToBottomCommand);
bindingSet.Apply();
}
}
}
Running debug, the WebViewContent gets exact Url but I don't know why it doesn't work as I thought: "<iframe width=\"100%\" height=\"100%\" src=\"https://www.youtube.com/embed/usoFYAqOMyA\" frameborder=\"0\" allowfullscreen></iframe>"
Does anyone has idea?
Thanks
Done a lot of researching and I have finally solved the issue.
All you need is putting SetWebChromeClient(new WebChromeClient()); before LoadData
public BindableWebView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
WebSettings settings = this.Settings;
settings.JavaScriptEnabled = true;
SetWebChromeClient(new WebChromeClient());
}
private string _webViewContent;
public string WebViewContent
{
get { return _webViewContent; }
set
{
_webViewContent = value;
LoadHtmlString();
}
}
private void LoadHtmlString()
{
LoadData(WebViewContent, "text/html", "utf-8");
}
Related
I am using a spinner and an arrayAdapter to populate items in spinner. I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
My code is as below,
adapterList= new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemMultipleChoice);
spnMultiTest.Adapter = adapterList;
I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
Basically I just translate the codes from Android Spinner with multiple choice
to Xamarin codes. I have tested it and it works fine:
MultiSpinner.cs:
public interface MultiSpinnerListener
{
void onItemsSelected(bool[] selected);
}
public class MultiSpinner : Spinner, IDialogInterfaceOnMultiChoiceClickListener, IDialogInterfaceOnCancelListener
{
Context _context;
private List<String> items;
private bool[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) : base(context)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1) : base(context, arg1)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1, int arg2) : base(context, arg1, arg2)
{
_context = context;
}
public void OnClick(IDialogInterface dialog, int which, bool isChecked)
{
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
public override void OnClick(IDialogInterface dialog, int which)
{
dialog.Cancel();
}
public override bool PerformClick()
{
AlertDialog.Builder builder = new AlertDialog.Builder(_context);
builder.SetMultiChoiceItems(
items.ToArray(), selected, this);
builder.SetPositiveButton("OK",this);
builder.SetOnCancelListener(this);
builder.Show();
return true;
}
public void SetItems(List<String> items, String allText,
MultiSpinnerListener listener)
{
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new bool[items.Count];
for (int i = 0; i < selected.Length; i++)
selected[i] = true;
ArrayAdapter<string> adapter = new ArrayAdapter<string>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { allText });
// all text on the spinner
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item, new String[] { allText });
Adapter = adapter;
}
public void OnCancel(IDialogInterface dialog)
{
Java.Lang.StringBuffer spinnerBuffer = new Java.Lang.StringBuffer();
bool someUnselected = false;
for (int i = 0; i < items.Count; i++)
{
if (selected[i] == true)
{
spinnerBuffer.Append(items[i]);
spinnerBuffer.Append(", ");
}
else
{
someUnselected = true;
}
}
String spinnerText;
if (someUnselected)
{
spinnerText = spinnerBuffer.ToString();
if (spinnerText.Length > 2)
spinnerText = spinnerText.Substring(0, spinnerText.Length - 2);
}
else
{
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { spinnerText });
Adapter = adapter;
if (listener != null)
{
listener.onItemsSelected(selected);
}
}
}
simple_spinner_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Update:
Here is the codes for using this MultiSpinner:
Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<MultiSpinner.MultiSpinner
android:id="#+id/mSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity.cs:
public class MainActivity : Activity
{
MultiSpinner mSpinner;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
mSpinner = FindViewById<MultiSpinner>(Resource.Id.mSpinner);
List<string> items = new List<string> {
"Android",
"iOS",
"UWP"
};
mSpinner.SetItems(items, "AllText", null);
}
}
I am new to MvvmCross, I have a question with regards to binding in Android. I can bind to single property but unable to data bind to an object. Not sure what I am doing wrong but here it is:
Model class:
public class Login : MvxNotifyPropertyChanged
{
private string _email;
public string Email
{
get { return _email; }
set
{
SetProperty(ref _email, value);
}
}
public string Password { get; set; }
}
Snippet of View Model Class:
public class LoginOptionViewModel: MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public LoginOptionViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
LoginCommand =
new MvxAsyncCommand(async () => await _navigationService.Navigate<RegistrationViewModel>());
}
public IMvxAsyncCommand LoginCommand { get; set; }
private Login _loginInfo;
public Login LoginInfo
{
get
{
return _loginInfo ?? new Login();
}
set
{
_loginInfo = value;
RaisePropertyChanged(() => LoginInfo);
}
}
}
Snippet of Android Axml:
<EditText
android:id="#+id/loginEmailTxt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/EmailHint"
android:textColor="#color/darkNavy"
android:inputType="textEmailAddress"
local:MvxBind="Text LoginInfo.Email" />
Where am I going wrong, I have placed a breakpoint but do not see it being hit. This is binded to EditText but nothing appears to happen. Am I missing or doing something wrong in order to bind to an object property ?
MvvmCross object data binding
You could implement the MvxNotifyPropertyChanged interface, so the system could notifies clients that a property value has changed.
Modify your object to :
public class Login : MvxNotifyPropertyChanged
{
private string _email;
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
}
In the MainViewModel :
private Login _login;
public Login Login
{
get
{
return _login ?? new Login() { Email = "=-="};
}
set
{
_login = value;
RaisePropertyChanged(() => Login);
}
}
Then use it in axml :
local:MvxBind="Text Login.Email"
It works fine on my side.
Update :
I cant reproduce your problem, but here is my complete code, hope this can help you :
public class MainViewModel : MvxViewModel
{
public MainViewModel()
{
}
public override Task Initialize()
{
return base.Initialize();
}
public IMvxCommand ResetTextCommand => new MvxCommand(ResetText);
private void ResetText()
{
Text = "Hello MvvmCross";
}
private string _text = "Hello MvvmCross";
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}
private Login _login;
public Login Login
{
get
{
return _login ?? new Login() { Email = "=-="};
}
set
{
_login = value;
RaisePropertyChanged(() => Login);
}
}
}
public class Login : MvxNotifyPropertyChanged
{
private string _email;
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
}
Effect.
I am populating a firebase data with firebase recycler adapter through databinding and want to populate images with the data either from Picasso or Glide, but unable to do. So can any one help me what to write and where to write the code of either Picasso or Glide.
public class ShopByCategoryFragment extends Fragment {
FirebaseRecyclerAdapter adapter;
Firebase mFirebaseRef = new Firebase("https://abc.firebaseio.com/").child("subCategories");
public ShopByCategoryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.recycler_view, container, false);
final RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
adapter = new FirebaseRecyclerAdapter<SubCategories, ViewHolder>(SubCategories.class, R.layout.fragment_shop_by_category,
ViewHolder.class, mFirebaseRef) {
#Override
protected void populateViewHolder(ViewHolder viewHolder, SubCategories subCategories, int i) {
FragmentShopByCategoryBinding binding = viewHolder.getBinding();
binding.setSubCategories(subCategories);
}
};
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
return rootView;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public FragmentShopByCategoryBinding binding;
public ViewHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
public FragmentShopByCategoryBinding getBinding() {
return binding;
}
}
#Override
public void onDestroy() {
super.onDestroy();
adapter.cleanup();
}
}
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="SubCategories"
type="com.abc.www.shopping.model.SubCategories"/>
</data>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:id="#+id/sub_category_image"
android:scaleType="centerCrop"
android:src="#{SubCategories.image}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:text="#{SubCategories.title}"
android:id="#+id/sub_category_title"
android:padding="16dp"
android:textColor="#color/colorSubCategoryTitle"
android:layout_gravity="center"
android:textAppearance="#android:style/TextAppearance.Material.Title"
tools:targetApi="lollipop" />
</FrameLayout>
</android.support.v7.widget.CardView>
public class SubCategories extends BaseObservable {
private String title;
private String image;
public SubCategories() {
// empty constructor
}
public SubCategories(String title, String image) {
this.title = title;
this.image = image;
}
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
}
I have made something similar to what you need. I'll share my code and do a little explanation.
I have a class that returns the FirebaseRecyclerAdapter like this:
public RecyclerView.Adapter postAdapter(String userKey, final Context context, MyClickListener clickListener) {
Query userPostRef = postRef.child(userKey);
myClickListener = clickListener;
return new FirebaseRecyclerAdapter<PostModel, DataObjectHolder>(PostModel.class, R.layout.stream_layout, DataObjectHolder.class, userPostRef) {
#Override
public void populateViewHolder(final DataObjectHolder dataviewHolder, final PostModel postModel, int position) {
StorageReference mStorageRef = FirebaseStorage.getInstance().getReference();
mStorageRef.child("uploaded_captures/" + postModel.getImageFilename() + ".jpg").getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
dataviewHolder.setImage(uri.toString(), context);
dataviewHolder.setComment(postModel.getPostComment());
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
Log.e("FirebaseRecyclerAdapter", exception.getMessage());
}
});
}
};
}
and a ViewHolder that loads the image into the ImageView used by the layout:
private static class DataObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View mView;
public DataObjectHolder(View itemView) {
super(itemView);
mView = itemView;
}
void setImage(final String imageURL, final Context context) {
final ImageView postImage = (ImageView) mView.findViewById(R.id.imageView);
if (BuildConfig.DEBUG) {
Picasso.with(context).setIndicatorsEnabled(true);
}
Picasso.with(context)
.load(imageURL)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(context)
.load(imageURL)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage);
}
});
}
void setComment(String text) {
TextView comment = (TextView) mView.findViewById(R.id.comment);
comment.setText(text);
}
#Override
public void onClick(View v) {
myClickListener.onItemClick(getAdapterPosition(), v);
}
}
On Picasso I'm using the NerworkPolicy.OFFLINE to check if the image is on the disk cache before trying to get it from the network. It's not perfect and I'm still having some issues with the delay of the images once the ViewHolder it's been recycled but I think it's a start for you to go on.
In my ViewHolder I do this by:
Glide.with(context)
.load(chat.getImageUrl())
.into(imageView);
I haven't tried using data binding for this yet, but the code is simple enough.
I'm currently creating an app for Android using MvvmCross. Part of that app requires a MvxListView, where each item has 2 TextViews. One of these TextViews are hidden by default. I'm looking to implement an accordion like functionality, where clicking on the first TextView will show/hide the other TextView.
I've currently gotten most of this to work with the MvvmCross Visibility Plugin, but the click event is bound to the MvxListView instead of the TextView inside it. What I've currently gotten to work looks like this:
FirstViewModel:
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<Interest>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
interest._parent = this;
Interests.Add(interest);
}
var pluginLoader = new PluginLoader();
pluginLoader.EnsureLoaded();
}
private ObservableCollection<Interest> _interests;
public ObservableCollection<Interest> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public ICommand ItemVisibleCommand
{
get
{
return new MvxCommand<Interest>(item => item.IsVisible = !item.IsVisible);
}
}
}
FirstView:
<?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"
...>
<Mvx.MvxListView
...
local:MvxBind="ItemsSource Interests"
local:MvxItemTemplate="#layout/item_interests" />
</LinearLayout>
item_interests:
<?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="30dp"
local:MvxBind="Text InterestName" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
local:MvxBind="Text InterestDescription; Visibility IsVisible, Converter=Visibility" />
</LinearLayout>
In order to bind it to the TextView inside the MvxListView, I've been trying to modify my code to something similar to How to bind ItemClick in MvxListView in MvxListView as per the anwser by Stuart, resulting in the following code:
item_interest:
<?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="30dp"
local:MvxBind="Text InterestName; Click ItemVisibleCommand" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
local:MvxBind="Text InterestDescription; Visibility IsVisible, Converter=Visibility" />
</LinearLayout>
FirstViewModel:
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<Interest>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
interest._parent = this;
Interests.Add(interest);
}
var pluginLoader = new PluginLoader();
pluginLoader.EnsureLoaded();
}
private ObservableCollection<Interest> _interests;
public ObservableCollection<Interest> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public void MakeItemVisible(bool isVisible)
{
isVisible = !isVisible;
}
Interest:
public class Interest : INotifyPropertyChanged
{
public string InterestId { get; set; }
public string InterestName { get; set; }
public string InterestDescription { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public FirstViewModel _parent { get; set; }
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
onPropertyChanged(this, "IsVisible");
}
}
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
public Interest(string id, string name, string description)
{
//Initialisers
}
private MvxCommand<bool> _itemVisible;
public ICommand ItemVisibleCommand
{
get
{
_itemVisible = _itemVisible ?? new MvxCommand<bool>(IsVisible => _parent.MakeItemVisible(IsVisible));
return _itemVisible;
}
}
}
resulting in the following exception:
04-04 15:05:40.575 I/MonoDroid(18011): UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
04-04 15:05:40.575 I/MonoDroid(18011): at Cirrious.MvvmCross.ViewModels.MvxCommand`1<bool>.Execute (object) <IL 0x00010, 0x00088>
04-04 15:05:40.575 I/MonoDroid(18011): at Cirrious.MvvmCross.Binding.Droid.Target.MvxViewClickBinding.ViewOnClick (object,System.EventArgs) <IL 0x0001f, 0x000fb>
04-04 15:05:40.575 I/MonoDroid(18011): at Android.Views.View/IOnClickListenerImplementor.OnClick (Android.Views.View) [0x0000d] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/a1e3982a/source/monodroid/src/Mono.Android/platforms/android-15/src/generated/Android.Views.View.cs:1615
04-04 15:05:40.575 I/MonoDroid(18011): at Android.Views.View/IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (intptr,intptr,intptr) [0x00011] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/a1e3982a/source/monodroid/src/Mono.Android/platforms/android-15/src/generated/Android.Views.View.cs:1582
04-04 15:05:40.575 I/MonoDroid(18011): at (wrapper dynamic-method) object.a963c1ac-b573-4022-b41d-f0f002438c84 (intptr,intptr,intptr) <IL 0x00017, 0x00043>
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
Thanks in advance to anyone who's taken the time to read all that :)
UPDATE - I tried to do as Stuart suggested, and got the following solution:
First off, to preserve the original Interest entity, it got wrapped in an InterestWrapper.
public class InterestWrapper : INotifyPropertyChanged
{
private Interest _interest;
private InterestAndroidViewModel _parent; //TO-DO
public Interest Item { get { return _interest; } }
public event PropertyChangedEventHandler PropertyChanged;
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
onPropertyChanged(this, "IsVisible");
}
}
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
public InterestWrapper(Interest interest, InterestAndroidViewModel parent)
{
IsVisible = false;
_interest = interest;
_parent = parent;
}
public IMvxCommand ItemVisibleCommand
{
get
{
return new MvxCommand(() => _parent.MakeItemVisible(_interest));
}
}
}
FirstViewModel
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<InterestWrapper>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
InterestWrapper wrapper = new InterestWrapper(interest, this);
Interests.Add(wrapper);
}
}
private ObservableCollection<InterestWrapper> _interests;
public ObservableCollection<InterestWrapper> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public void MakeItemVisible(Interest interest)
{
if (interest.IsVisible)
{
interest.IsVisible = !interest.IsVisible;
}
else
{
foreach (var _interest in _interests)
{
_interest.Item.IsVisible = false;
}
interest.IsVisible = !interest.IsVisible;
}
}
}
item_interest:
<?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"
...
<RelativeLayout
...
<TextView
...
local:MvxBind="Text Item.InterestName; Click ItemVisibleCommand" />
<Mvx.MvxImageView
...
local:MvxBind="Visibility Item.IsVisible, Converter=InvertedVisibility; Click ShowEducationsCommand" />
</RelativeLayout>
</RelativeLayout>
The local:MvxBind="Text InterestName; Click ItemVisibleCommand" can only really call a non-parameterized MvxCommand - it can't call MvxCommand<bool> as it doesn't know what the bool value is.
If you wanted to, you could use the CommandParameter converter to pass in the value - e.g. local:MvxBind="Text InterestName; Click CommandParameter(ItemVisibleCommand, IsVisible)"
But overall, in this case I'd probably recommend rewriting ItemVisibleCommand as just a "toggle visible" command instead
Look through this question it's the as your one and has the solution suggested by Stuart, so think it's what you are looking for.
Binding button click in ListView template MvvMCross
In my example below I want to bind an ItemClick Command to the Item in the MvxListView.
Here I have in my ViewModel a List of Person that contains a List of Dog.
The ItemsSource HasDogs binding works fine.
When MvvmCross is trying to bind ItemClick SelectDogCommand to the ICommand in the Viewmodel I get this Exception.
[0:]
MvxBind:Warning: 11,30 Unable to bind: source property source not found Property:SelectDogCommand on Person
[0:] MvxBind:Warning: 11,30 Unable to bind: source property source not found Property:SelectDogCommand on Person
12-04 15:05:03.062 I/mono-stdout(16338): MvxBind:Warning: 11,30 Unable to bind: source property source not found Property:SelectDogCommand on Person
Hope you can Help.
Here is my example:
public class FirstViewModel:MvxViewModel
{
private List<Person> _persons;
public List<Person> Persons
{
get { return _persons; }
set { _persons = value; }
}
private Cirrious.MvvmCross.ViewModels.MvxCommand<Dog> _selectDog;
public System.Windows.Input.ICommand SelectDogCommand
{
get
{
_selectDog = _selectDog ?? new Cirrious.MvvmCross.ViewModels.MvxCommand<Dog>(SelectDog);
return _selectDog;
}
}
private void SelectDog(Dog item)
{
ShowViewModel<DetailViewModel>(new DetailViewModel.Parameters{dog = item});
}
}
public class Person
{
private string _name;
private List<Dog> _hasDogs;
public List<Dog> HasDogs
{
get { return _hasDogs; }
set { _hasDogs = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
public class Dog{...}
The Android View Xml:
FirstView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
...>
<TextView ...
local:MvxBind="Text Persons"
<Mvx.MvxListView
...
local:MvxBind="ItemsSource Persons"
local:MvxItemTemplate="#layout/item_person" />
</LinearLayout>
item_person:
<?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_height="200dp">
<TextView
...
local:MvxBind="Text Name" />
<Mvx.MvxListView
...
local:MvxBind="ItemsSource HasDogs; ItemClick SelectDogCommand"
local:MvxItemTemplate="#layout/item_dog" />
</LinearLayout>
The DataContext for your person list item is a Person - so your SelectDogCommand needs to be part of the Person class - e.g. something like:
public class Person
{
private string _name;
private List<Dog> _hasDogs;
public List<Dog> HasDogs
{
get { return _hasDogs; }
set { _hasDogs = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
private Cirrious.MvvmCross.ViewModels.MvxCommand<Dog> _selectDog;
public System.Windows.Input.ICommand SelectDogCommand
{
get
{
_selectDog = _selectDog ?? new Cirrious.MvvmCross.ViewModels.MvxCommand<Dog>(dog => _parent.SelectDog(dog));
return _selectDog;
}
}
private FirstViewModel _parent;
public Person(FirstViewModel parent)
{
_parent = parent;
}
}
or alternatively you could get Person to inherit from MvxNavigatingObject (or MvxPropertyChanged or MvxViewModel) - in which case the ShowViewModel methods will be available there too.