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);
Related
Note: These are randomly generated addresses
Hey Guys, Learning Xamarin and I am trying to scroll my Frame Layour down and reveal a search bar for my list view. Here is what is happening:
I color coded my layouts to see if the sizes where a problem, but I dont think they are since my orange layout is plenty big to hold my two entries. Am I using the wrong layouts for this kind of application? I would appreciate any help!
Here is my translation code:
frameLayout.Animate().TranslationYBy(editSearch.Height).SetDuration(500).Start();
And my layout file:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/swipeLayout">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="100dp"
android:id="#+id/frameLayoutParent"
android:background="#android:color/holo_green_dark">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frameLayout"
android:background="#android:color/holo_green_light">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="300dp"
android:background="#android:color/holo_orange_light">
<Button
android:text="Add New Address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/addNewAddress"
android:maxHeight="20dp" />
</LinearLayout>
<ListView
android:paddingTop="50dp"
android:minWidth="25px"
android:minHeight="300dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/myListView"
android:maxHeight="300dp"/>
</FrameLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editSearch"
android:hint="Search ZipCodes"
android:textColor="#000"/>
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
EDIT:
Here is my OnOptionsItemSelected Code, where my animation is triggered.
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.action_search:
//search icon has been clicked
if (isAnimating)
return true;
else
{
if (animateBool)
{
//list view is up
animation anim = new animation(myListView, myListView.Height - editSearch.Height);
anim.Duration = 500;
myListView.StartAnimation(anim);
anim.AnimationStart += Anim_AnimationStartDown; //listener for when animation has started
anim.AnimationEnd += Anim_AnimationEndDown;
classSwipeRefresh.Animate().TranslationYBy(editSearch.Height).SetDuration(500).Start();
}
else
{
animation anim = new animation(myListView, myListView.Height + editSearch.Height);
anim.Duration = 500;
myListView.StartAnimation(anim);
anim.AnimationStart += Anim_AnimationStartUp; //listener for when animation has started
anim.AnimationEnd += Anim_AnimationEndUp;
classSwipeRefresh.Animate().TranslationYBy(-editSearch.Height).SetDuration(500).Start();
}
animateBool = !animateBool;
return true;
}
default:
return base.OnOptionsItemSelected(item);
}
}
Learning Xamarin and I am trying to scroll my Frame Layour down and reveal a search bar for my list view. Here is what is happening:
I use your code to test, but I have no problem when translating by Y in SwipeRefreshLayout__Refresh event.
public class MainActivity : AppCompatActivity
{
string[] items;
ListView listview1;
SwipeRefreshLayout swiplayout;
FrameLayout framelayout;
EditText edittext;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
listview1 = FindViewById<ListView>(Resource.Id.myListView);
swiplayout = FindViewById<SwipeRefreshLayout>(Resource.Id.swipeLayout);
framelayout = FindViewById<FrameLayout>(Resource.Id.frameLayout);
edittext = FindViewById<EditText>(Resource.Id.editSearch);
swiplayout.Refresh += Swiplayout_Refresh;
items = new string[] { "Vegetables", "Fruits", "Flower Buds", "Legumes", "Bulbs", "Tubers" };
listview1.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, items);
}
private void Swiplayout_Refresh(object sender, EventArgs e)
{
framelayout.Animate().TranslationYBy(edittext.Height).SetDuration(500).Start();
}
}
This is my screenshot:
If you want to filter ListView, I suggest you can use SearchView in Toolbar to filter listview data, please take a look this sample:
https://github.com/Cheesebaron/SearchView-Sample/tree/master/SearchViewSample
I found many similar questions on StackOverflow, but can't seem to figure out this issue.
I'm trying to bind an ObservableCollection to a ListView so that when the contents of the collection change the ListView will automatically update. Unfortunately, when elements are added to the collection (via button click), the ListView isn't being updated. How can I make the ListView reflect the current contents of the collection?
Note: It seems that if I force layout with listView.RequestLayout(); the listView will contain the correct number of items. However, it won't necessarily contain the right items. For example, if I delete the first item and add a new one at the end, forcing a layout has no effect. If I force the layout after deleting the first item, then again after adding one at the end, the contents are correct.
Activity code
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
ObservableCollection<UserTask> allTasksCollection = new ObservableCollection<UserTask>();
while(count < 6)
{
allTasksCollection.Add(new UserTask("Task number " + count));
count++;
}
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Bind listview to all tasks
ListView listView = FindViewById<ListView>(Resource.Id.allTasksListView);
UserTaskListAdapter adapter = new UserTaskListAdapter(this, allTasksCollection);
listView.Adapter = adapter;
// Button click should add a new task and remove the first task.
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
allTasksCollection.Add(new UserTask("Task number " + count));
button.Text = string.Format("{0} tasks!", count++);
};
}
}
Adapter
public class UserTaskListAdapter : BaseAdapter<UserTask>
{
Activity context;
ObservableCollection<UserTask> list;
public UserTaskListAdapter(Activity _context, ObservableCollection<UserTask> _list)
: base()
{
this.context = _context;
this.list = _list;
}
public override int Count
{
get { return list.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override UserTask this[int index]
{
get { return list[index]; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(Resource.Layout.UserTaskRowItem, parent, false);
UserTask item = this[position];
view.FindViewById<TextView>(Resource.Id.Title).Text = item.Name;
return view;
}
}
Main XAML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/rootLayout">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/allTasksContainer">
<Button
android:id="#+id/myButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/hello" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/allTasksListView" />
</LinearLayout>
</LinearLayout>
ListItem XAML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/linearLayoutHorizontal">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/CheckboxContainer">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/checkboxSelect" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/TextContainer">
<TextView
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/Title" />
<TextView
android:text="Small Text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/DueDate" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
Try to call NotifyDataSetChanged() on your adapter after updating its items, AFAIK ListView in Android doesn't observe the changes on your ObservableCollection:
Try this code:
button.Click += delegate {
allTasksCollection.Add(new UserTask("Task number " + count));
button.Text = string.Format("{0} tasks!", count++);
adapter.NotifyDataSetChanged();
};
You can also try to add a litener to your ObservableCollection, in your adapter, change the constructor to this:
this.list.CollectionChanged += (sender,args) => { NotifyDataSetChanged(); };
Take a look at the INotifyPropertyChanged interface. It will warn the listview that variables within an object within the listview have changed.
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/
Edit: misread your question. My bad.
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"
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.
I am trying to create a cell which has 2 buttons side by side.
I have done this in MT.D : How can I get an element with 2 buttons side by side?
I now need to do this for the droid version.
Looking at the source for ButtonElement in the MvvmCross code I can see that Element has consideration for 1 click event only.
What is the best way of going about this for MD.D? It looks completely different (obviously) to the touch counterpart.
I can create a layout that has the 2 buttons in but I have no way of hooking the buttons up to click events.
Here is the layout code (dialog_double_button.xml):
<?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:id="#+id/LinearLayout02"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true">
<Button
android:id="#+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" android:text="button 1">
</Button>
<Button
android:id="#+id/Button03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="button 2">
</Button>
</LinearLayout>
And my element looks like :
public class DoubleButton : Element, View.IOnClickListener
{
public DoubleButton (): base("", "dialog_double_button")
{
}
protected override View GetViewImpl (Context context, ViewGroup parent)
{
var view = DroidResources.LoadButtonLayout(context, parent, LayoutName);
if (view != null)
{
var buttons = view.FindViewById<Button> (Resource.Layout.dialog_double_button);
}
return view;
}
public void OnClick(View v)
{
if (Click != null)
Click(this, EventArgs.Empty);
if (SelectedCommand != null)
{
// TODO should we have a SelectedCommandParameter here?
if (SelectedCommand.CanExecute(null))
{
SelectedCommand.Execute(null);
}
}
}
}
When I run this code with:
[Activity(Label = "Login")]
public class Login : MvxDialogActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Root = new RootElement{
new Section{
new DoubleButton()
}
};
}
}
I can see the layout is added to the UI but inside my elements c# code calling var buttons = view.FindViewById<Button> (Resource.Layout.dialog_double_button); yields nothing - so I can't hook the click events up.
Any ideas?
view.FindViewById will only work if view its the parent of that buttons. In your case it is not, it's the parent of "LinearLayout02", so you should get it first and use FindViewById on it.
(OP amending this answer as it was nearly what I ended up with)
I actually fixed it using:
var button1 = view.FindViewById<Button>(context.Resources.GetIdentifier("Button02", "id", context.PackageName));
var button2 = view.FindViewById<Button>(context.Resources.GetIdentifier("Button03", "id", context.PackageName));