-
Notifications
You must be signed in to change notification settings - Fork 528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Xamarin.Android.Build.Tasks] Fragments & CodeBehind #1302
Conversation
} | ||
} | ||
|
||
static readonly Dictionary<string, Func<Widget, XmlReader, Widget>> WidgetCreators = new Dictionary<string, Func<Widget, XmlReader, Widget>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A small nit - create the dictionary with StringComparer.Ordinal
, it's going to be faster
@jonpryor apparently if you nest widgets in android they can have the same id. not sure how we will deal with that? |
I'm not sure I like the idea of removing widget nesting. Why it does look nicer, it creates a problem with conflicting/unreachable widgets that share the ID but are nested in different parents within the same layout. Android supports this scenario (it also supports sibling widgets with the same ID) as weird as it is. By removing nesting we'll either generate bad managed code (two or more properties with the same name) or will have to make only one of the nested widgets bound to a property and that is an unjustified limitation IMO. I remember testing nesting quite extensively when I worked on the original POC and it worked pretty well. Perhaps the bug is somewhere else and we don't need to remove support for this? |
A side note - I'm wondering if we really need CodeDOM here... The reason why we used it in the first place was to be able to generate F# and C# code-behind, but with the (necessary, alas, due to CodeDOM limitations) literal snippets this goal is not achievable |
@grendello noted;
It is achievable, in theory: we "just" need to pass in the language we're generating for into the The larger problem is that, afaik, F# doesn't support |
Is this actually a worry?
How, and what does it look like? Best I can quickly suss out is these two methods of consequence: class android.app.Activity {
public <T extends View> T findViewById (int);
}
class android.view.View {
public final <T extends View> T findViewById (int);
} If you have the "same" ID in a Given that, does it really make sense to expose "every" View with the same ID? This doesn't seem like a good construct to support. Additionally...why was I getting nested types in the first place for this example? The Finally, I don't understand how it's usable, regardless. <TextView
android:id="@+id/first_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:editable="false"
/> The expectation then, is that "somehow" I should have a property named partial class MainActivity {
public @__first_text_view_Views first_text_view { get; }
public sealed class @__first_text_view_Views {
}
} Nothing within Background: here is the (not compiling!) file that was generated before this PR/fix: //------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Xamarin.Android.RuntimeTests {
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<__first_text_view_Views> @__first_text_viewFunc;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
private @__first_text_view_Views @__first_text_view;
#line default
#line hidden
partial void OnLayoutViewNotFound<T> (int resourceId, ref T type) where T : global::Android.Views.View;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
public @__first_text_view_Views first_text_view {
get {
if ((this.@__first_text_viewFunc == null)) {
this.@__first_text_viewFunc = this.@__CreateClass___first_text_view_Views;
}
return this.@__EnsureView<@__first_text_view_Views>(this.@__first_text_viewFunc, ref this.@__first_text_view);
}
}
#line default
#line hidden
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 @__first_text_view_Views @__CreateClass___first_text_view_Views() {
return new @__first_text_view_Views(this);
}
public sealed class @__first_text_view_Views {
private MainActivity @__parent;
private Func<__second_text_view_Views> @__second_text_viewFunc;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
private @__second_text_view_Views @__second_text_view;
#line default
#line hidden
public @__first_text_view_Views(MainActivity @__parent) {
this.@__parent = @__parent;
}
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
public @__second_text_view_Views second_text_view {
get {
if ((this.@__second_text_viewFunc == null)) {
this.@__second_text_viewFunc = this.@__CreateClass___second_text_view_Views;
}
return this.@__parent.@__EnsureView<@__second_text_view_Views>(this.@__second_text_viewFunc, ref this.@__second_text_view);
}
}
#line default
#line hidden
private @__second_text_view_Views @__CreateClass___second_text_view_Views() {
return new @__second_text_view_Views(this);
}
public sealed class @__second_text_view_Views {
private MainActivity @__parent;
private Func<__csharp_simple_fragment_Views> @__csharp_simple_fragmentFunc;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
private @__csharp_simple_fragment_Views @__csharp_simple_fragment;
#line default
#line hidden
public @__second_text_view_Views(MainActivity @__parent) {
this.@__parent = @__parent;
}
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
public @__csharp_simple_fragment_Views csharp_simple_fragment {
get {
if ((this.@__csharp_simple_fragmentFunc == null)) {
this.@__csharp_simple_fragmentFunc = this.@__CreateClass___csharp_simple_fragment_Views;
}
return this.@__parent.@__EnsureView<@__csharp_simple_fragment_Views>(this.@__csharp_simple_fragmentFunc, ref this.@__csharp_simple_fragment);
}
}
#line default
#line hidden
private @__csharp_simple_fragment_Views @__CreateClass___csharp_simple_fragment_Views() {
return new @__csharp_simple_fragment_Views(this);
}
public sealed class @__csharp_simple_fragment_Views {
private MainActivity @__parent;
private Func<__csharp_partial_assembly_Views> @__csharp_partial_assemblyFunc;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
private @__csharp_partial_assembly_Views @__csharp_partial_assembly;
#line default
#line hidden
public @__csharp_simple_fragment_Views(MainActivity @__parent) {
this.@__parent = @__parent;
}
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
public @__csharp_partial_assembly_Views csharp_partial_assembly {
get {
if ((this.@__csharp_partial_assemblyFunc == null)) {
this.@__csharp_partial_assemblyFunc = this.@__CreateClass___csharp_partial_assembly_Views;
}
return this.@__parent.@__EnsureView<@__csharp_partial_assembly_Views>(this.@__csharp_partial_assemblyFunc, ref this.@__csharp_partial_assembly);
}
}
#line default
#line hidden
private @__csharp_partial_assembly_Views @__CreateClass___csharp_partial_assembly_Views() {
return new @__csharp_partial_assembly_Views(this);
}
public sealed class @__csharp_partial_assembly_Views {
private MainActivity @__parent;
private Func<fragment> @__csharp_full_assemblyFunc;
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
private fragment @__csharp_full_assembly;
#line default
#line hidden
public @__csharp_partial_assembly_Views(MainActivity @__parent) {
this.@__parent = @__parent;
}
#line 1 "/Volumes/Seagate4TB/work/xamarin-android/src/Mono.Android/Test/Resources/layout/Main.axml"
public fragment csharp_full_assembly {
get {
if ((this.@__csharp_full_assemblyFunc == null)) {
this.@__csharp_full_assemblyFunc = this.@__Create_csharp_full_assembly;
}
return this.@__parent.@__EnsureView<fragment>(this.@__csharp_full_assemblyFunc, ref this.@__csharp_full_assembly);
}
}
#line default
#line hidden
private fragment @__Create_csharp_full_assembly() {
return this.@__parent.@__FindView<fragment>(this.@__parent, Resource.Id.csharp_full_assembly);
}
}
}
}
}
}
} |
Indeed, however that is far from the CodeDOM "spirit" and leads to hackish and polluted code (what if we want to support VB for instance). I'm wondering if we wouldn't be better off writing simple language generators ourselves, after all the generated code is not at all complicated (and we could take advantage of modern language features, as a bonus)
That might be a problem, yes, but it's not insurmountable. Both WinForms and ASP.NET generated code that consisted of two parts - one for the user to modify, another not to be modified by the user. Our current partial methods could become empty stubs in a separate .fs file and/or we could provide a template that already has them right in the main activity class. |
Maybe yes, maybe no - but since it's a valid scenario in Android we should support it (especially that we can), if only to not create corner cases.
Yes, you can access the duplicated ID elements by first finding their parents and then looking for the ID of the child. Granted, it's not "sane" code, but it's valid and there's no reason for us not to support it.
Correct.
We can't judge what is good or wrong - it's valid code and it can happen in the wild (cut-n-paste coding, duplication of similar sections of code to build an interface - I can easily imagine that being done). If anything, our nested properties model would make accessing such widgets easier for the developer. Certainly much better than saying "hey, we can't give you the other nested widget with the same ID, you need to use
That might be a bug in the generator code.
This looks like another bug in the generator code... Lamentably, I lost my original code with my notebook and the only copy we have is @Redth's which I'm not sure was the latest/best version I had :( However, I can't imagine not implementing access to the actual widget in the generated code... :) |
While trying to repro Issue dotnet#1296, I had a brilliant idea: let's use the new CodeBehind support from 7c31899! Everything then fell apart. For starters, Issue dotnet#1296 deals with Fragments, and 7c31899 didn't support the presence of `<fragment/>` within Layout files. Plumb support for that, by adding: partial class MainActivity { partial void OnLayoutFragmentNotFound<T> (int resourceId, ref T type) where T : global::Android.App.Fragment; } Another issue was noticed as well: all the line numbers in the `#line` pragmas were always `1`. This was narrowed down to a bug in `GetLineInfo()`. Then there's an issue with where I was using the CodeBehind file: within the `src/Mono.Android/Test` project, which is in the `Xamarin.Android.RuntimeTests` namespace. This prompted all manner of namespace resolution failures: error CS0234: The type or namespace name 'App' does not exist in the namespace 'Xamarin.Android' (are you missing an assembly reference?) ... Fix this by using `CodeTypeReferenceOptions.GlobalReference` as much as is practical (which isn't enough), and by "kludging" up the `CodeNamespaceImport` construction so that we instead emit: using global::System; Finally, the generated codebehind had some weird nesting going on: partial class MainActivity { public __first_text_view_Views first_text_view {get;} public sealed partial class __first_text_view_Views { public __second_text_view_Views second_text_view {get;} public sealed partial class __second_text_view_Views { public sealed partial class __csharp_simple_fragment_Views { public sealed partial class __csharp_partial_assembly_Views { } } } } } It was bizarre *and* unusable: the `second_text_view` property -- corresponding to `<TextView android:id="@+id/second_text_view" />` -- was *nested within* a generated `MainActivity.__first_text_view_Views` type, and I'm not sure why that existed. Furthermore, it *can't* work, as the generated `MainActivity.__first_text_view_Views.__CreateClass___second_text_view_Views()` method depends on a constructor which doesn't exist: private @__second_text_view_Views @__CreateClass___second_text_view_Views() { // There *is* a __second_text_view_Views(Activity) constructor, // but not __second_text_view_Views(__first_text_view_Views). return new @__second_text_view_Views(this); } I "solved" (?) this by updating `LoadWidgets()` so that it always used `widgetRoot` for all recursive calls to `LoadWidgets()`, so that there is only ever one root. This removed all the nested types.
dfcb2c0
to
c2f3c99
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should merge this PR - I think it'd be better if we fixed the nested properties generation properly as there are legitimate uses for layouts with duplicate widget IDs in subtrees and we should expose that in the generated code,
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Fragment handling code partially taken from dotnet#1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById` instead of the usual `Activity.FindViewById` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
) Fragment handling code partially taken from: #1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById()` instead of the usual `Activity.FindViewById()` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
Superseded by PR #1378. |
) Fragment handling code partially taken from: #1302 Fragments are treated as normal widget, with the difference that they have to be found using `FragmentManager.FindFragmentById()` instead of the usual `Activity.FindViewById()` method. Each generated class also gets a property named `Widget` which returns the actual Android widget as found in the layout file. This allows us to keep hierarchical nature of the XML code (thus handling nested widgets with duplicate ids gracefully) while being able to access the parent widget itself. The commit introduces a new attribute called `tools:managedType` which is used to specify the element associated property's type. We cannot use `tools:class` for this purpose since the layout root element already uses it to specify the name of the generated code-behind class and if the element had `android:id` on it, we would end up using the activity's type for the root element's property in the generated code, instead of its actual type.
While trying to repro Issue #1296, I had a brilliant idea: let's use
the new CodeBehind support from 7c31899!
Everything then fell apart.
For starters, Issue #1296 deals with Fragments, and 7c31899 didn't
support the presence of
<fragment/>
within Layout files.Plumb support for that, by adding:
Another issue was noticed as well: all the line numbers in the
#line
pragmas were always
1
. This was narrowed down to a bug inGetLineInfo()
.Then there's an issue with where I was using the CodeBehind file:
within the
src/Mono.Android/Test
project, which is in theXamarin.Android.RuntimeTests
namespace. This prompted all manner ofnamespace resolution failures:
Fix this by using
CodeTypeReferenceOptions.GlobalReference
as muchas is practical (which isn't enough), and by "kludging" up the
CodeNamespaceImport
construction so that we instead emit:Finally, the generated codebehind had some weird nesting going on:
It was bizarre and unusable: the
second_text_view
property --corresponding to
<TextView android:id="@+id/second_text_view" />
--was nested within a generated
MainActivity.__first_text_view_Views
type, and I'm not sure why that existed. Furthermore, it can't work,
as the generated
MainActivity.__first_text_view_Views.__CreateClass___second_text_view_Views()
method depends on a constructor which doesn't exist:
I "solved" (?) this by updating
LoadWidgets()
so that it always usedwidgetRoot
for all recursive calls toLoadWidgets()
, so that thereis only ever one root. This removed all the nested types.