Search code examples
xamlxamarin.formsdata-bindingmessage

Updating control value in listview through messagecenter


I want to update value in entry control inside ListView from another Pop up page using messageCenter approach. It is not working as expected as it is updating multiple items in list view.

My base page (PageA) has ListView

       <ListView
        x:Name="workList"
        Grid.Row="2"
        SeparatorColor="{DynamicResource AccentColor}"
    ItemsSource="{ Binding WorkItems}"                   
        Margin="5"
        CachingStrategy="RecycleElement"
        RowHeight="440"
        SeparatorVisibility="Default"
        SelectionMode="None"
        HasUnevenRows="False">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <local:LoadItemPutawayTemplate />
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

LoadItemPutAwayTemplate is my content view for ListView as below

     <?xml version="1.0" encoding="UTF-8"?>
     <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     xmlns:d="http://xamarin.com/schemas/2014/forms/design"
     xmlns:mc="http://schemas.openxmlformats.org/markup- 
     compatibility/2006"
     mc:Ignorable="d"
     x:Class="Sanipex.LoadItemPutawayTemplate">

     <Grid
      RowSpacing="0"
      Padding="0"
      Margin="0,10,0,0"
      >

      <Grid.RowDefinitions>
      <RowDefinition
        Height="*" />
       <RowDefinition
        Height="auto" />
      </Grid.RowDefinitions>

        <Entry
            x:Name="OverrideLoc"
             Grid.Row="0"
            TextColor="Black"
            WidthRequest="110"
            Text="{Binding toLocation}"
            HorizontalOptions="StartAndExpand"
            VerticalOptions="Center"
            FontAttributes="Bold"
            PlaceholderColor="Black"
            FontSize="20"/>

         <Button
                Grid.Row="1"
        HorizontalOptions="Center"
                x:Name="LocationBtn"
        Text="Override Loc"
                BackgroundColor="#25D366"
                TextColor="{DynamicResource AccentColor}"
                BorderColor="#25D366"
        Style="{ StaticResource CircleActionButtonFlatStyle }"
                Clicked="LocationBtn_Clicked"/>
       </Grid>

On LocationBtn_Clicked i am calling the popup

  private async void LocationBtn_Clicked(object sender, EventArgs e)
  {
        var AvailableLocationPopUp = new AvailableLocationsPopUp();
        await PopupNavigation.Instance.PushAsync(AvailableLocationPopUp);
  }

Below is my Pop up page (AvailableLocationsPopUp) button click method from where I am sending the message

    private void PutawayBtn_Clicked(object sender, EventArgs e)
    {
      MessagingCenter.Send<AvailableLocationsPopUp, string>(this, 
     "Location", "XYZ");
    }

As i have entry control in my contentView of LoadItemPutAwayTemplate, I am subscribing message in its constructor as below to update the value of entry control

    public LoadItemPutawayTemplate()
    {
        InitializeComponent();

        MessagingCenter.Subscribe<AvailableLocationsPopUp, string>(this, 
        "Location", async (sender, arg) =>
        {
            this.OverrideLoc.Text = arg;
        }
    }

But this is updating values to all ListView items which are showing on UI. I just want to update the value of item from where the pop up was initiated.


Solution

  • When you subscribe to the message in the template, then all the ListView items will receive the message. I have created a simple demo to simulate your app,the main code is:

    LocationModel.cs

    public class LocationModel: INotifyPropertyChanged
    {
         string toLocation;
    
        public string ToLocation {
            set { SetProperty(ref toLocation, value); }
            get { return toLocation; }
        }
    
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;
    
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    LoadItemPutawayTemplate.xaml.cs

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class LoadItemPutawayTemplate : ContentView
    {
        public LoadItemPutawayTemplate()
        {
            InitializeComponent();
        }
    
        private void LocationBtn_Clicked(object sender, EventArgs e)
        {
            LocationModel stringInThisCell = (LocationModel)((Button)sender).BindingContext;
    
            System.Diagnostics.Debug.WriteLine("send  msg  current update = " + stringInThisCell.ToLocation);
    
            string source = stringInThisCell.ToLocation;
            string result = "XYZ";
    
            string[] values = { source, result };
    
            MessagingCenter.Send<LoadItemPutawayTemplate, string[]>(this, "Location", values);
        }
    }
    

    LoadItemPutawayTemplate.xaml

    <ContentView.Content>
        <Grid
             RowSpacing="0"
             Padding="0"
             Margin="0,2,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="100" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition  Width="*"/>
                <ColumnDefinition  Width="*"/>
            </Grid.ColumnDefinitions>
    
            <Entry
            x:Name="OverrideLoc"
            Grid.Column="0"
            TextColor="Black"
            WidthRequest="110"
            Text="{Binding ToLocation}"
            HorizontalOptions="StartAndExpand"
            VerticalOptions="Center"
            FontAttributes="Bold"
            PlaceholderColor="Black"
            FontSize="20"/>
            <Button
                Grid.Column="1"
                HorizontalOptions="Center"
                x:Name="LocationBtn"
                Text="Override Loc"
                BackgroundColor="#25D366"
                TextColor="{DynamicResource AccentColor}"
                BorderColor="#25D366"
                Clicked="LocationBtn_Clicked"/>
        </Grid>
    
    </ContentView.Content>
    

    MainPage.xaml.cs

     public partial class MainPage : ContentPage
    {
        public List<LocationModel> WorkItems { get; set; }
        public MainPage()
        {
            InitializeComponent();
    
            WorkItems = new List<LocationModel>();
    
            WorkItems.Add(new LocationModel { ToLocation = "Tomato"});
            WorkItems.Add(new LocationModel { ToLocation = "Zucchini" });
            WorkItems.Add(new LocationModel { ToLocation = "Tomato2"});
            WorkItems.Add(new LocationModel { ToLocation = "Romaine2" });
            WorkItems.Add(new LocationModel { ToLocation = "Zucchin2"});
    
            MessagingCenter.Subscribe<LoadItemPutawayTemplate, string[]>(this,
             "Location", async (sender, values) =>
            {
                string source = values[0];
                string result = values[1];
    
                for (int i = 0; i < WorkItems.Count; i++)
                {
                    LocationModel model = WorkItems[i];
                    if (source.Equals(model.ToLocation)) {
                        model.ToLocation = result;
                        break;
                    }
                }
            });
    
            workList.ItemsSource = WorkItems;     
        }
    }
    

    Note:

    1. Since I don't know the code of AvailableLocationsPopUp, so I send the message when I click the Button.
    2. To simply the UI,I modified the UI a little in some places(xaml).
    3. You can get the full demo here.

    The result is : enter image description here