xamarin mvvmcross binding relative layout - xamarin

I am new to mvvmcross. I do not know how to bind touch event of a relative layout to a view model. Can someone help me on this. Thanks in advance.

This is possible using standard MvvmCross code. For the viewmodel you can use something like:
public class SomeViewModel : MvxViewModel
{
public IMvxCommand OpenSomethingCommand { get; private set; }
public SomeViewModel ()
{
OpenSomethingCommand = new MvxCommand(() => ShowViewModel<SomeOtherNewViewModel>());
}
}
Then for the layout in your android project, you can use:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
local:MvxBind="Click OpenSomethingCommand"
android:id="#+id/someLayout">
//Wrap any other layouts in here
</RelativeLayout>

Related

Xamarin latest update findviewbyid returns null

I just installed the latest update of Xamarin for Visual Studio.
Ever since, android can't find the views in my activity layouts.
Apparently the auto-generated IDs aren't updated correctly when i just save my resources.
If I clean the project and rebuild, my app runs fine. But after like 20 minutes of coding the problem will occur again.
Simple code: Resources/Layout/Main.axml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="9dp"
android:paddingBottom="9dp">
<Button
android:text="#string/action_stockcount"
android:layout_width="match_parent"
android:layout_height="90dp"
android:id="#+id/btnStockCount"
android:layout_marginLeft="18dp"
android:layout_marginRight="18dp"
android:layout_marginStart="9dp"
android:layout_marginTop="9dp"
android:textColor="#color/menu_button_text"
android:textSize="30dp"
android:drawableLeft="#drawable/menu_stockcount_big"
android:paddingLeft="18dp"
android:paddingRight="18dp"
android:paddingBottom="9dp"
android:paddingTop="9dp" />
</LinearLayout>
</ScrollView>
MainActivity.designer.cs:
using ...
namespace MyApp.Droid.Activities
{
public partial class MainActivity
{
private Button btnStockCount;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
SetTitle(Resource.String.ApplicationName);
btnStockCount = FindViewById<Button>(Resource.Id.btnStockCount);
// From time to time, this gives a NullReferenceException
btnStockCount.Click += BtnStockCount_Click;
InitComplete(savedInstanceState);
}
}
}
MainActivity.cs (Not quite important):
using ...
namespace MyApp.Droid.Activities
{
public partial class MainActivity : Activity
{
private void BtnStockCount_Click(object sender, EventArgs e)
{
StartActivity(typeof(IndexStockCountActivity));
}
}
}
I know how android works, I know how Xamarin works. Why would the latest update of Xamarin cause this issue (the IDs that aren't updated correctly when saving a resource)? What can I do to solve this issue?
It's really annoying to have to debug my project, notice that again it's not working well, and having to clean and rebuild, and notice that after that it IS working.
I also already noticed that when it's not working, after I cleaned and rebuilded the Resource Ids are in fact changed.
Cheers

MvvmCross creating custom binding to LinearLayout

I am trying to create a custom binding to LinearLayout to allow me create a view dynamically and bind this as a child to LinearLayout. For anyone familiar with WPF, this is similar to the functionality provided by the ContentControl or any WPF control deriving from ContentControl. Basically you can create your dynamic content and bind to the Content property of a ContentControl.
Here's what I have for the custom binding:
public class MvxLinearLayoutContentTargetBinding : MvxPropertyInfoTargetBinding<LinearLayout>
{
public MvxLinearLayoutContentTargetBinding(object target, PropertyInfo targetPropertyInfo) : base(target, targetPropertyInfo)
{
}
protected override void SetValueImpl(object target, object value)
{
base.SetValueImpl(target, value);
var view = target as LinearLayout;
if (view == null) return;
view.AddView((View)value);
}
public override Type TargetType
{
get { return typeof(LinearLayout); }
}
}
Here's how I am attempting to use this new binding in my 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="wrap_content"
android:paddingBottom="3dp"
android:paddingTop="3dp"
android:paddingLeft="5dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="3dp"
android:paddingTop="3dp"
android:paddingLeft="0dp"
local:MvxBind="Content CustomView">
</LinearLayout>
</LinearLayout>
Any my ViewModel looks like this:
public class CustomViewModel : MvxViewModel
{
public object CustomView { get; set; }
}
The custom binding has also been registered in Setup.cs as follows:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterPropertyInfoBindingFactory(
typeof(MvxLinearLayoutContentTargetBinding),
typeof(LinearLayout), "Content");
base.FillTargetFactories(registry);
}
Yet with all this in place, I do not see my view.
MvvmCross already supports a primitive version of this. Although without DataTemplateSelector.
You can bind a collection of ViewModels to MvxLinearLayout.ItemsSource. You also need to remember to set a ItemTemplateId:
<MvxLinearLayout
...
local:MvxItemTemplateId="#layout/layout_to_repeat"
local:MvxBind="ItemsSource ViewModelCollection"
/>
This is however super inefficient as it does not recycle views etc. So if you need a variant that supports DataTemplateSelector, then use MvxRecyclerView instead.

Prism and Xamarin.Forms: IApplicationLifecycle

I have a viewmodel that inherits from BindableBase and implements IApplicationLifecycle. However, IApplicationLifecycle.OnResume() and IApplicationLifecycle.OnSleep() is never called on neither iOS nor android.
How do you use IApplicationLifecycle? I cant seem to find any documentation for it. Thanks in advance!
In my App.xaml.cs i have:
NavigationService.NavigateAsync("NavigationPage/MainPage");
and my MainPage.xaml looks like this:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PrismApp.Views;assembly=PrismApp"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="PrismApp.Views.MainPage"
Title="MainPage">
<TabbedPage.Children>
<local:APage Icon="tabicon_mapspage.png" />
<local:BPage Icon="tabicon_profilepage.png" />
<local:CPage Icon="tabicon_statuspage.png" />
</TabbedPage.Children>
Finally my MainPageViewModel looks like this:
public class MainPageViewModel : BindableBase, IApplicationLifecycle
{
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public void OnResume()
{
var debug = 42;
}
public void OnSleep()
{
var debug = 42;
}
}
I have put a minimal project of it here: https://github.com/RandomStuffAndCode/PrismApp
Since you are in a TabbedPage the IApplicationAware methods are only being invoked on the TabbedPage.CurrentPage (SelectedTab), as that is the current active page.
By the way, you can change this behavior by overriding the OnResume and OnSleep methods in your app class to achieve your goal. Use the existing code as a guide: https://github.com/PrismLibrary/Prism/blob/master/Source/Xamarin/Prism.Forms/PrismApplicationBase.cs#L171

PrismDryIoc.Forms AutowireViewModel not working

I created a Prism Unity App (from the prism extension pack - really nice), added 2 new views and set up basic navigation between the views. This all worked fine without any hiccups.
What I really want is to use DryIoc (company policy). So I proceeded to remove the unity packages (Unity and Prism.Unity.Forms) and installed the DryIoc Packages (DryIoc and Prism.DryIoc.Forms). Fixed the App.xaml to use the correct namespace (xmlns:prism="clr-namespace:Prism.DryIoc;assembly=Prism.DryIoc.Forms") and alos fixed all the other references to use DryIoc and not the Unity References.
This all compiles and runs without any exceptions. But when I debug it is clear that the AutoWireup does not work as expected (at least as I expect it to). The breakpoint in the ViewModel's constructor (or any other place) is not hit and the Title bindings does not pull through.
Is there a Configuration/Setup or reference that I am missing?
My Code:
App.xaml:
<?xml version="1.0" encoding="utf-8" ?><prism:PrismApplication xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.DryIoc;assembly=Prism.DryIoc.Forms"
x:Class="XamFormsPrism.App"></prism:PrismApplication>
App.xaml.cs:
public partial class App : Prism.DryIoc.PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void ConfigureContainer()
{
base.ConfigureContainer();
}
protected override void OnInitialized()
{
this.InitializeComponent();
this.NavigationService.NavigateAsync(NavigationLinks.MainPage);
}
protected override void RegisterTypes()
{
this.Container.RegisterTypeForNavigation<MainPage>();
this.Container.RegisterTypeForNavigation<ViewA>();
this.Container.RegisterTypeForNavigation<ViewB>();
}
}
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XamFormsPrism.Views.MainPage"
Title="MainPage"><StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding Title}" />
<Button Text="{Binding NavigateText}" Command="{Binding NavigateCommand}" /> </StackLayout></ContentPage>
MainPageViewModel.cs:
public class MainPageViewModel : BindableBase, INavigationAware
{
private INavigationService navigationService = null;
private string _title = "Jose Cuervo";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public DelegateCommand NavigateCommand { get; private set; }
public MainPageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
this.NavigateCommand = new DelegateCommand(this.navigate, this.canNavigate);
}
private void navigate()
{
this.navigationService.NavigateAsync("ViewA");
}
private bool canNavigate()
{
return true;
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters.ContainsKey("title"))
Title = (string)parameters["title"] + " and Prism";
}
}
As seen above I use the standard naming convention of PageName & PageNameViewModel with the AutoWireUp set to True.
This all worked fine with Unity but I am missing something in DryIoc...but what escapes me.
I have searched the entire project, there is no reference or trace left of Unity.
Any help is much appreciated.
I finally had time again to have a look at this. If I explicit register the ViewModels in the container everything works fine. So I am guessing that there is either a rule missing for the DryIoC container or when using DryIoC you have to explicitly register the ViewModels.

Custom ToggleButton findViewById Null Pointer

I have been trying to solve this problem for hours now and have exhausted all the obvious solutions. I have a custom toggle button that works fine as long as I don't try to get it using findViewById
Here is the relevant code:
public class WeekButton extends ToggleButton {
private static int mWidth;
public WeekButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WeekButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
...
}
Here is the layout..
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<org.myname.projetname.reminders.WeekButton.WeekButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mondayToggleButton"
style="#style/RecurrenceDayOfWeekStyle"
android:textOff=" Mon"
android:textOn=" Mon" />
</LinearLayout>
Exception occurs here..
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_reminder_activity);
//This causes null pointer
WeekButton weekButton = (WeekButton)findViewById(R.id.mondayToggleButton));
}
Thanks for any help you can provide!
SOLUTION: Turns out I forgot to instantiate the list I was adding the WeekButtons which made it look like the call to find id was working but it was. Thank you everyone you tried to help.
Didn't it show any error before run? xml name space attribute is given for the parent tag.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<org.myname.projetname.reminders.WeekButton.WeekButton
android:id="#+id/mondayToggleButton"
style="#style/RecurrenceDayOfWeekStyle"
android:textOff=" Mon"
android:textOn=" Mon" />
</LinearLayout>
Also, Cast to your class on findViewById().
Forgot to instantiate a list I was adding the weekbuttons to.

Resources