You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Xamarin.Android.Build.Tasks] Add Support for CodeBehind for layout files (#1238)
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);
}
}
}
0 commit comments