locked
How do I set the x:Name property of a UI control that I created in code? RRS feed

  • Question

  • User372459 posted

    I want to create a control at runtime, and then refer to that control in code. I can set other properties of controls that I create, but not Name nor x:Name. I can set the StyleId in code when creating a control, but can StyleId then be used in code later to identify the control and perform actions to it?

    Friday, August 17, 2018 10:19 PM

Answers

  • User89714 posted

    @ScottPendleton said: So the variable "label" is reused and goes out of scope, thus I lose that instance when the for loop ends. (Right?)

    Yes. It's standard C# stuff rather than anything specific to Xamarin.Forms .

    @ScottPendleton said: add them to an array of Labels

    Yes, that would be fine, as long as the array remains in scope.

    @ScottPendleton said: Since the Labels are children of the FlexLayout named "A", can I just treat that FlexLayout object as an array and set properties like this: A.Child[0].Text = "Hello, World" ?

    Layout types use an ObservableCollection, so yes.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Saturday, August 18, 2018 5:47 PM
  • User89714 posted

    @ScottPendleton said: Just to be clear -- nobody had an answer for how to set the x:Name of a control that was generated from C# during page initialization.

    Both @LuisDavidDelaCruz and I said to just instantiate the appropriate subclass and to use the resulting C# reference/variable. @LuisDavidDelaCruz was more explicit than me about x: name being the name of the variable.

    In more detail, what happens with XAML is this:

    Given the XAML in PageView.xaml: <Label x:Name="Geronimo" HorizontalOptions="Center" Text="Hello" VerticalOptions="Center" />

    When it is built, a generated C# file PageView.xaml.g.cs (that you don't modify manually) is generated. That file contains: public partial class PageView

    and

    private global::Xamarin.Forms.Label Geronimo;

    and

    Geronimo = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Label>(this, "Geronimo");

    In PageView.xaml.cs (into which you put your own code), the remainder of the partial class called PageView is defined. In that class, you can use the Label Geronimo to do things such as:

    Geronimo.Text = "Here we go";

    So, what happens when x:Name is used in XAML is that a class is created (formed from two partial classes) that has a member variable with the specified name. So, if you work purely in C#, the equivalent is that you simply have a member variable with the desired name. In your particular scenario, using a collection will probably make more sense than using 32 named member variables.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Sunday, August 19, 2018 10:18 AM

All replies

  • User89714 posted

    @ScottPendleton - If you are working with C# (as I currently do) rather than XAML, why do you need a name associated with a control that you create at runtime? If you instantiate a class (such as a subclass of ContentView) you have the instance that you can reference in your C#, no name is required. If within your C# you want to be able to identify which classes are somehow related or have some capability, you can either have those classes implement an interface (where the interface can be empty and just used as a tag), or you can use the View class's ClassId property (actually it's in the Element class that View inherits from). Or you could add a Name property to an interface and have your various subclasses implement that interface. There are all sorts of possibilities.

    If I am misunderstanding your requirement, can you clarify further what you are trying to achieve.

    BTW - if you want to know what XF does with XAML, you can find out easily enough by looking at the XF source code. I haven't looked at that particular area of the code, but the LoadFromXaml code in ViewExtensions.cs, the ResourcesLoader class and the (possibly obsolete) XamlLoader class, would all be good places to start.

    Saturday, August 18, 2018 12:16 AM
  • User372459 posted

    @JohnHardman Here's the situation: I create 32 labels using C# at runtime, also setting dimensions, color, alignment properties at that time. Later a user selects a song from a list. The song has 32 bars of music. The app finds the guitar chords for accompanying the song and puts the chords into the appropriate label. In order to do that, the app has to know that the first chord goes into the first label, the second into the second label, and so on. Therefore I'd like to name the chords A1, A2, A3, etc. up to A32. And then I can assign the correct chord to the correct label. And then overwrite it all when the user selects a different song.

    So, while I'm creating the labels programmatically, what property can I set and then later use as an identifier when I need to modify the control later?

    If in XAML I give a label the x:Name = "A1", I can immediately refer to it in code, with intellisense and all. But if A1 doesn't exist till run time, and I try to write a method that refers to some control named A1, it won't compile.

    Saturday, August 18, 2018 3:59 AM
  • User102046 posted

    @ScottPendleton what if you add those labels and chords(i have no idea what they are) in separate list/array as well, in the same order in which you populate on screen. then you can use index

    Saturday, August 18, 2018 5:02 AM
  • User364658 posted

    @ScottPendleton When you create visual elements at runtime time the property x: name is the name of the variable, in example we have this in XAML:

    XAML x: Name = "ElementXAML"

    When you create them controls with C# code, your x: name is the name of your variable

    C#

        var ElementCSharp = new Label ();
    

    Or Label ElementCSharp = new Label (); then you can change the properties of the element with the variable name

    in example

    ElementCSharp.Text = "";
    ElementCSharp.BackgroudColor = Color.Black;
    

    the same as you do with the property x:name

    But a sad news is you can't dinamically assign the name with a for loop or another, but you could identify each label with another property like the text, the color or another property, Check out this answer to know more about it

    https://forums.xamarin.com/discussion/79924/how-to-check-the-text-property-of-a-label-easypeasy

    I hope it be useful for you!

    Saturday, August 18, 2018 5:26 AM
  • User89714 posted

    @ScottPendleton said: So, while I'm creating the labels programmatically, what property can I set and then later use as an identifier when I need to modify the control later?

    @JohnHardman said: If you instantiate a class (such as a subclass of ContentView) you have the instance that you can reference in your C#, no name is required

    As mentioned before, if working just in C#, you don't need a name property. The instance of the object is all you need, which you can hold in a variable with any name supported in C#, so A1-A32 are all fine (although you might want to make the A lower case).

    Saturday, August 18, 2018 8:23 AM
  • User372459 posted

    @Xami3, @LuisDavidDelaCruz, @JohnHardman Thank you for your insights. I have been creating the labels like this:

    int i = 1;
    for (i = 1; i < 33; i++) {
        Label label = new Label();
        label.WidthRequest = 80;
        (etc.)
        A.Children.Add(label); // A is a FlexLayout created with XAML, with x:Name = "A"
    }
    

    So the variable "label" is reused and goes out of scope, thus I lose that instance when the for loop ends. (Right?) To do what @JohnHardman says, I'd need a variable that stays in scope. Also, @LuisDavidDelaCruz notes that I can't dynamically assign the name of the variable. Maybe @Xami3 has the right solution for me: As my loop creates the Label objects, it should add them to an array of Labels. I can persist that array and refer to the elements by index, which suits me fine.

    That raises this question: Since the Labels are children of the FlexLayout named "A", can I just treat that FlexLayout object as an array and set properties like this: A.Child[0].Text = "Hello, World" ?

    Saturday, August 18, 2018 3:34 PM
  • User364658 posted

    @ScottPendleton You can access your instance if you create a global instance in the next way

    Label label = new Label();
                int i = 1;
                for (i = 1; i < 33; i++)
                {
                    label.WidthRequest = 80;
                    A.Children.Add(label); // A is a FlexLayout created with XAML, with x:Name = "A"
                }
    
    Saturday, August 18, 2018 3:59 PM
  • User89714 posted

    @ScottPendleton said: So the variable "label" is reused and goes out of scope, thus I lose that instance when the for loop ends. (Right?)

    Yes. It's standard C# stuff rather than anything specific to Xamarin.Forms .

    @ScottPendleton said: add them to an array of Labels

    Yes, that would be fine, as long as the array remains in scope.

    @ScottPendleton said: Since the Labels are children of the FlexLayout named "A", can I just treat that FlexLayout object as an array and set properties like this: A.Child[0].Text = "Hello, World" ?

    Layout types use an ObservableCollection, so yes.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Saturday, August 18, 2018 5:47 PM
  • User372459 posted

    @JohnHardman is correct, as this code shows:

            public partial class MainPage : ContentPage
            {
                public MainPage()
                {
                    InitializeComponent();
    
                    int i = 1;
                    for (i = 1; i< 33; i++)
                    {
                        Label label = new Label();
                        label.WidthRequest = 80;
                        label.HeightRequest = 20;
                        label.FontSize = 20;
                        label.TextColor = Color.Black;
                        label.BackgroundColor = Color.White;
                        label.Text = "A" + i.ToString();
                        label.Margin = 1;
                        A.Children.Add(label);
                    }
                    A.BackgroundColor = Color.Black;
                }
    
                private void Button_Clicked(object sender, EventArgs e)
                {
                    Label test = (Label)A.Children[5];
                    test.Text = "Hello, World!";
                    System.Diagnostics.Debug.WriteLine(test.Text);
                }
            }
    
    Saturday, August 18, 2018 9:25 PM
  • User372459 posted

    Just to be clear -- nobody had an answer for how to set the x:Name of a control that was generated from C# during page initialization. However, my actual problem was how to interact with an array (actually, an Observable Collection) of C#-created Labels. And now I know!

    Saturday, August 18, 2018 9:28 PM
  • User89714 posted

    @ScottPendleton said: Just to be clear -- nobody had an answer for how to set the x:Name of a control that was generated from C# during page initialization.

    Both @LuisDavidDelaCruz and I said to just instantiate the appropriate subclass and to use the resulting C# reference/variable. @LuisDavidDelaCruz was more explicit than me about x: name being the name of the variable.

    In more detail, what happens with XAML is this:

    Given the XAML in PageView.xaml: <Label x:Name="Geronimo" HorizontalOptions="Center" Text="Hello" VerticalOptions="Center" />

    When it is built, a generated C# file PageView.xaml.g.cs (that you don't modify manually) is generated. That file contains: public partial class PageView

    and

    private global::Xamarin.Forms.Label Geronimo;

    and

    Geronimo = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Label>(this, "Geronimo");

    In PageView.xaml.cs (into which you put your own code), the remainder of the partial class called PageView is defined. In that class, you can use the Label Geronimo to do things such as:

    Geronimo.Text = "Here we go";

    So, what happens when x:Name is used in XAML is that a class is created (formed from two partial classes) that has a member variable with the specified name. So, if you work purely in C#, the equivalent is that you simply have a member variable with the desired name. In your particular scenario, using a collection will probably make more sense than using 32 named member variables.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Sunday, August 19, 2018 10:18 AM