locked
how to access a control from an object method? RRS feed

  • Question

  • User396832 posted

    I'm about to badly embarrass my self here - being a complete newb to c# (& OO) and Xamarin.Forms (trying to learn too much in one go, I know) I'm trying to create a stack-layout of options that will contain e.g. labels and buttons in which I want to control the Text property of the buttons when any of them are clicked.

    I previously tried using databinding/itemsource on the stacklayout but got even more lost and have not been able to find examples I could make very much sense of - the documentation looks like it should be really simple, alas :-(

    In the code listed below I can access both the mapOptsCntrl object and its methods as well as the stack layout control via its x:Name from the event handler methods of buttons in the XAML. But if I want to change things based on programmatically created button having been clicked I'm kinda lost.

    I hope this is not too much code to post here: ` using System; using System.Collections.Generic; using System.Linq; using Xamarin.Forms;

    namespace AgentApp { public partial class zDevMapOptions_list : ContentPage {

        public class LocOption : Object
        {
            public string AddressLine { get; set; }
            public string State { get; set; }
        }
    
        public class MapOptsCntrl : Object
        {
            public Dictionary<string, LocOption> ListOptsContainer = new Dictionary<string, LocOption>();
    
            public void Create(int number)
            {
                for (int i = 0; i < number; i++)
                {
                    string key = i.ToString();
                    LocOption LocOption = new LocOption { AddressLine = "Addy str " + key, State = "Show", };
                    ListOptsContainer.Add(key, LocOption);
                }
            }
            public List<StackLayout> Draw()
            {
                int optsCount = ListOptsContainer.Count();
                List<StackLayout> listItems = new List<StackLayout>();
                for (int i = 0; i < optsCount; i++)
                {
                    string key = i.ToString();
                    Button listOptBtn = new Button { Text = ListOptsContainer[key].State + " " + key, };
                    listOptBtn.Clicked += (sender, args) => { this.Click(key); };
                    StackLayout listItem = new StackLayout
                    {
                        Orientation = StackOrientation.Horizontal,
                        Children = { new Label { Text = "Label " + key }, listOptBtn, },
                    };
                    listItems.Add(listItem);
                };
                return listItems;
            }
            public void Click(string key)
            {
                Console.WriteLine("Click " + key + " state is " + ListOptsContainer[key].State);
                int optsCount = ListOptsContainer.Count();
                for (int i = 0; i < optsCount; i++)
                {
                    string newState;
                    string thisKey = i.ToString();
                    if (i.ToString() == key)
                    {
                        string curState = ListOptsContainer[key].State;
                        newState = (curState == "Use") ? "Show" : "Use";
                    }
                    else { newState = "Show"; }
                    ListOptsContainer[thisKey].State = newState;
    
                }
                //  how to trigger a redraw?
                //// to efectively do what btn_redraw_Clicked does?
            }
        }
    
        MapOptsCntrl mapOptsCntrl = new MapOptsCntrl();
        public zDev_Map_Options_list()
        {
            InitializeComponent();
        }
    
        private void btn_go_Clicked(object sender, EventArgs e)
        {
            mapOptsCntrl.Create(3);
            List<StackLayout> listItems = mapOptsCntrl.Draw();
            OptionsListStack.Children.Clear();
            foreach (StackLayout item in listItems)
            {
                OptionsListStack.Children.Add(item);
            }
        }
        private void btn_redraw_Clicked(object sender, EventArgs e)
        {
            List<StackLayout> listItems = mapOptsCntrl.Draw();
            OptionsListStack.Children.Clear();
            foreach (StackLayout item in listItems)
            {
                OptionsListStack.Children.Add(item);
            }
        }
    }
    

    } `

    and the xaml:

    ```

            <Button x:Name="btn_go" Text="Create"  Grid.Row="1" Clicked="btn_go_Clicked"/>
            <Button x:Name="btn_redraw" Text="Redraw"  Grid.Row="2" Clicked="btn_redraw_Clicked"/>
    
            <StackLayout x:Name="OptionsListStack" Grid.Row="3"/>
        </Grid>
    </ContentPage.Content>
    

    ```

    Thanks in advance for any direction or help!

    Tuesday, September 15, 2020 1:55 PM

Answers

  • User369979 posted

    We could get the button through the click's sender parameter. Pass it to the event so that we could manipulate it.

    listOptBtn.Clicked += (sender, args) => { this.Click(key, (Button)sender); };
    

    The first parameter here is the button itself. And then we could change its text in the Click method like: ``` public void Click(string key, Button sender) { Console.WriteLine("Click " + key + " state is " + ListOptsContainer[key].State); int optsCount = ListOptsContainer.Count(); for (int i = 0; i < optsCount; i++) { string newState; string thisKey = i.ToString(); if (i.ToString() == key) { string curState = ListOptsContainer[key].State; newState = (curState == "Use") ? "Show" : "Use"; } else { newState = "Show"; } ListOptsContainer[thisKey].State = newState;

    }
    //  how to trigger a redraw?
    //// to efectively do what btn_redraw_Clicked does?
    ///
    sender.Text = ListOptsContainer[key].State;
    
    StackLayout layout = (StackLayout)sender.Parent;
    foreach (object child in layout.Children)
    {
        if (child is Label label)
        {
            label.Text = ListOptsContainer[key].State;
        }
    }
    

    }

    ``` I got the label located in the same layout as the button here. We could change the label's value as I posted above. Here is the effect:

    However, I highly recommend you to use a listview to display a set of data with the same template: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/ Change the binding's value to manipulate the corresponding item's. This is much easier to be achieved without so much complicated logic.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 16, 2020 2:20 AM
  • User369979 posted

    would you recommend going (or attempting) the MVVM route? Yes, you could have a try.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 16, 2020 6:10 AM

All replies

  • User369979 posted

    We could get the button through the click's sender parameter. Pass it to the event so that we could manipulate it.

    listOptBtn.Clicked += (sender, args) => { this.Click(key, (Button)sender); };
    

    The first parameter here is the button itself. And then we could change its text in the Click method like: ``` public void Click(string key, Button sender) { Console.WriteLine("Click " + key + " state is " + ListOptsContainer[key].State); int optsCount = ListOptsContainer.Count(); for (int i = 0; i < optsCount; i++) { string newState; string thisKey = i.ToString(); if (i.ToString() == key) { string curState = ListOptsContainer[key].State; newState = (curState == "Use") ? "Show" : "Use"; } else { newState = "Show"; } ListOptsContainer[thisKey].State = newState;

    }
    //  how to trigger a redraw?
    //// to efectively do what btn_redraw_Clicked does?
    ///
    sender.Text = ListOptsContainer[key].State;
    
    StackLayout layout = (StackLayout)sender.Parent;
    foreach (object child in layout.Children)
    {
        if (child is Label label)
        {
            label.Text = ListOptsContainer[key].State;
        }
    }
    

    }

    ``` I got the label located in the same layout as the button here. We could change the label's value as I posted above. Here is the effect:

    However, I highly recommend you to use a listview to display a set of data with the same template: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/ Change the binding's value to manipulate the corresponding item's. This is much easier to be achieved without so much complicated logic.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 16, 2020 2:20 AM
  • User396832 posted

    Awesome, thank you so much!

    However, I highly recommend you to use a listview to display a set of data with the same template: ... would you recommend going (or attempting) the MVVM route?

    Wednesday, September 16, 2020 6:09 AM
  • User369979 posted

    would you recommend going (or attempting) the MVVM route? Yes, you could have a try.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 16, 2020 6:10 AM