Support generating CodeBehind files for `.axml` layout files. This
removes the need to call `FindViewById<T>()` manually, as properties
generated into the CodeBehind file will call it instead.
The CodeBehind files are generated for all `.axml` files with a build
action of `@(AndroidResource)` *and* contain an XML namespace to
`http://schemas.android.com/tools` and a `tools:class` attribute
that contains the name of the `partial class` to generate:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:tools="http://schemas.xamarin.com/android/tools"
tools:class="UnnamedProject.MainActivity">
<Button
android:id="@+id/myButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
The generated CodeBehind partial class will use the value of the
`//@tools:class` attribute, and will contain a property for each
element with an `//@android:id` attribute. The `//@android:id`
attribute is the name of a generated field, while the containing
element name is the type of the property.
The above XML fragment would partially produce:
partial class MainActivity {
// Call instead of `SetContentView()`
private void InitializeContentView ();
// Called if `myButton` can't be found
partial void OnLayoutViewNotFound<T> (int resourceId, ref T type)
where T : global::Android.Views.View;
// One property per //@android:id
// Throws InvalidOperationException if it can't be found
public Button myButton { get; }
}
This allows replacing the current code pattern of:
SetContentView (Resource.Layout.NameOfLayoutFilename);
var button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
};
and instead rely on the CodeBehind properties:
InitializeContentView ();
myButton.Click += delegate {
};
There are two other requirements to make this work:
1. All declarations of the class specified by the `//@tools:class`
attribute must be `partial` classes, otherwise a CS0260 error
will be produced at build time.
2. `InitializeContentView()` must be called instead of
`SetContentView()`. `InitializeContentView()` itself calls
`SetContentView()`, providing the `@(AndroidResource)` filename
as the layout to use.
This feature was originally prototyped by @grendello.
The following is a sample of the kind of code which will be
generated by this new system:
namespace UnnamedProject {
using System;
using Android.App;
using Android.Widget;
using Android.Views;
using Android.OS;
// Generated from layout file 'Resources/layout/Main.axml'
public partial class MainActivity {
private Func<Button> @__myButtonFunc;
private Button @__myButton;
partial void OnLayoutViewNotFound<T> (int resourceId, ref T type)
where T : global::Android.Views.View;
public Button myButton {
get {
if (@__myButtonFunc == null) {
@__myButtonFunc = this.@__Create_myButton;
}
return this.@__EnsureView<Button>(this.@__myButtonFunc, ref this.@__myButton);
}
}
private void InitializeContentView() {
this.SetContentView(Resource.Layout.Main);
}
private T @__FindView<T>(global::Android.Views.View parentView, int resourceId)
where T : global::Android.Views.View
{
T view = parentView.FindViewById<T>(resourceId);
if ((view == null)) {
this.OnLayoutViewNotFound(resourceId, ref view);
}
if ((view != null)) {
return view;
}
throw new System.InvalidOperationException($"View not found (ID: {resourceId})");
}
private T @__FindView<T>(global::Android.App.Activity parentView, int resourceId)
where T : global::Android.Views.View
{
T view = parentView.FindViewById<T>(resourceId);
if ((view == null)) {
this.OnLayoutViewNotFound(resourceId, ref view);
}
if ((view != null)) {
return view;
}
throw new System.InvalidOperationException($"View not found (ID: {resourceId})");
}
private T @__FindView<T>(global::Android.App.Fragment parentView, int resourceId)
where T : global::Android.Views.View
{
return this.@__FindView<T>(parentView.Activity, resourceId);
}
private T @__EnsureView<T>(System.Func<T> creator, ref T field)
where T : class
{
if ((field != null)) {
return field;
}
if ((creator == null)) {
throw new System.ArgumentNullException(nameof (creator));
}
field = creator();
return field;
}
private Button @__Create_myButton() {
return this.@__FindView<Button>(this, Resource.Id.myButton);
}
}
}