Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

jonpryor
Copy link
Member

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:

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.

}
}

static readonly Dictionary<string, Func<Widget, XmlReader, Widget>> WidgetCreators = new Dictionary<string, Func<Widget, XmlReader, Widget>> {
Copy link
Contributor

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

@dellis1972
Copy link
Contributor

@jonpryor apparently if you nest widgets in android they can have the same id. not sure how we will deal with that?

@grendello
Copy link
Contributor

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?

@grendello
Copy link
Contributor

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

@jonpryor
Copy link
Member Author

@grendello noted;

with the ... literal snippets [using CodeDom] is not achievable

It is achievable, in theory: we "just" need to pass in the language we're generating for into the <GenerateCodeBehindForLayout/> task, and it can emit different snippets depending on language.

The larger problem is that, afaik, F# doesn't support partial methods. I'm thus not sure what to do wrt F# support. It would be good to hear from an actual F# user. :-)

@nosami? Thoughts? See also 7c31899.

@jonpryor
Copy link
Member Author

it creates a problem with conflicting/unreachable widgets that share the ID but are nested in different parents within the same layout.

Is this actually a worry?

Android supports this scenario

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 layout.xml file and call Activity.findViewById(int), you can only get one of them. (Presumably the top-level?) If you want the other, you need to use View.findViewById(int), or so I'm currently assuming.

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 Main.axml file doesn't reuse identifiers!

Finally, I don't understand how it's usable, regardless. Main.axml has:

  <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 first_text_view through which I can obtain a TextView. Instead, I got:

partial class MainActivity {
    public @__first_text_view_Views first_text_view { get; }

    public sealed class @__first_text_view_Views {
    }
}

Nothing within MainActivity. __first_text_view_Views gives me a TextView. In fact, TextView isn't even a token that appears within the generated file!

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);
                        }
                    }
                }
            }
        }
    }
}

@grendello
Copy link
Contributor

with the ... literal snippets [using CodeDom] is not achievable

It is achievable, in theory: we "just" need to pass in the language we're generating for into the task, and it can emit different snippets depending on language.

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)

The larger problem is that, afaik, F# doesn't support partial methods. I'm thus not sure what to do wrt F# support. It would be good to hear from an actual F# user. :-)

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.

@grendello
Copy link
Contributor

Is this actually a worry?

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.

Android supports this scenario

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 findViewById (int);
}
class android.view.View {
public final T findViewById (int);
}

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.

If you have the "same" ID in a layout.xml file and call Activity.findViewById(int), you can only get one of them. (Presumably the top-level?) If you want the other, you need to use View.findViewById(int), or so I'm currently assuming.

Correct.

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.

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 FindViewById - that would kind of defy the reason for this code-behind model :)

Additionally...why was I getting nested types in the first place for this example? The Main.axml file doesn't reuse identifiers!

That might be a bug in the generator code.

Finally, I don't understand how it's usable, regardless. Main.axml has:

The expectation then, is that "somehow" I should have a property named first_text_view through which I can obtain a TextView. Instead, I got:

partial class MainActivity {
public @__first_text_view_Views first_text_view { get; }

public sealed class @__first_text_view_Views {
}

}

Nothing within MainActivity. __first_text_view_Views gives me a TextView. In fact, TextView isn't even a token that appears within the generated file!

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.
Copy link
Contributor

@grendello grendello left a 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,

grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 7, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 8, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 9, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 9, 2018
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.
grendello added a commit to grendello/xamarin-android that referenced this pull request Mar 9, 2018
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.
jonpryor pushed a commit that referenced this pull request Mar 9, 2018
)

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.
@jonpryor
Copy link
Member Author

jonpryor commented Mar 9, 2018

Superseded by PR #1378.

@jonpryor jonpryor closed this Mar 9, 2018
jonpryor pushed a commit that referenced this pull request Mar 9, 2018
)

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.
@dellis1972 dellis1972 mentioned this pull request May 17, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Feb 4, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants