3
votes

I'm facing an issue in Xamarin forms Mvvm. I have 2 different layouts say Layout1 and Layout2 which are bounded with a common ViewModel. Layout1 contains multiple Labels which I'm generating dynamically using for loop in xaml.cs file and bind each Label'sTextProperty using SetBinding. Layout2 contain a button.

Now I want to change Text of a particular Label when button clicked.

Layout1.xaml

<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Layout1">

<StackLayout x:Name="ParentStack">

 // dynamic Labels to be added here..

</StackLayout>           
</StackLayout>

Layout1.xaml.cs

 public partial class Layout1: StackLayout
{
    public Label dummyLabel;
    public Layout1()
    {
        InitializeComponent();
        for (int i = 0; i < 3; i++)
         {
         dummyLabel= new Label
         {
           Text = " ",  
         };
         dummyLabel.SetBinding (Label.TextProperty,"PhaseValue");
         parentRowCells.Children.Add(dummyLabel);
         var tapGestureRecognizer_1 = new TapGestureRecognizer();                
            tapGestureRecognizer_1.SetBinding(TapGestureRecognizer.CommandProperty,"LabelClicked");
            tapGestureRecognizer_1.CommandParameter = dummyLabel;
            dummyLabel.GestureRecognizers.Add(tapGestureRecognizer_1);
             }

          }

        }

Layout2.Xaml

<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
 x:Class="Layout2">

<StackLayout x:Name="ParentStack">
<Button Command={Binding ButtonClickedCommand} Text="Click Me"  />
</StackLayout>           

</StackLayout>

ViewModel.cs

 class ViewModel
{

    public Label label = new Label();
    public string textstring = "new text string";
    ICommand _labelClicked;
    public ICommand LabelClicked
    {
        get
        {
            this._labelClicked= this._labelClicked?? new Command(s =>
            {

                label = s as Label;
             label.Text = "new text"; //this change the text of particular              label when clicked but i need it from button clicked event from another layout.
                // here I'm getting the instance of label which i clicked on label.

            });

            return this._labelClicked;
        }
    }

    public ICommand ButtonClickedCommand{ protected set; get; }



    public ViewModel()
    {

        this.ButtonClickCommand = new Command<Button>((key) =>
        {

        //here I want to change the value of label when button command is clicked.  
         aa.Text = "this is not changing the text";
        });

    }



}

Any help in this or do I need to follow some other pattern..??

1

1 Answers

1
votes

My first thought would be to add each Label that you add to a List<Label> somewhere that you can access from both layouts... your View Model would seem like the logical place. Then when you click your button, you can find the particular Label whose text you want to change and change it. You will likely then have to reload your list.

However I think that a better way would be to use a ListView instead of a StackLayout. Then you can have an ItemTemplate for the ListView that includes one Label. You can then set up an ObservableCollection<T> of objects to use as the ListView.ItemsSource. You would want to make some custom object that has a Text property, or whatever you want to call the property that will hold the text for the Labels. It is better to use an object for the T in ObservableCollection<T> rather than using ObservableCollection<string> because changes to a string type will not be reflected in the ListView item, but changes to a property of an object (assuming of course that you make it a Bindable Property) will be reflected in those controls that are bound to that property. So in a nutshell, something like (in your ViewModel):

// Class level variable
ObservableCollection<CustomType> dummyLabelContents;

// then either in the ViewModel constructor or somewhere else:
dummyLabelContents = new ObservableCollection<CustomType>();

CustomType dummyText;
for (int i = 0; i < 3; i++)
{
    dummyText = new CustomType
    {
        Text = " ",  
    };
}
dummyLabelContents.Add(dummyText);

And your CustomType would just be a simple class with only a BindableProperty called Text.

Set up like this, you can assign your ListView.ItemsSource to be the dummyLabelContents ObservableCollection and then whenever you add an item to the ObservableCollection, the ListView will update automatically. Also, since using a custom type with a bindable text property in the ObservableCollection, when that text property is changed the item in the ListView should also update accordingly.