locked
Pass listItem data using command from button in MVVM RRS feed

  • Question

  • User368008 posted

    I am working on an app that will allow the user to pay for a Utility Bill. It allows for multiple utility accounts to be linked to one user. I have a list view that properly displays the information, in each ListItem Template i need to pass the value from the label to the ViewModel using a button. If this is ansered else where please let me know, i have searched for days and nothing so far has worked.

    Here is my View: <ContentPage.BindingContext> <local:UtilityViewModel/> </ContentPage.BindingContext> <StackLayout> <ListView x:Name="UtilityAccts" ItemsSource="{Binding Items}" HasUnevenRows="true" SelectedItem="{Binding SelectedItem}" SeparatorVisibility="Default" SeparatorColor="#0b4996" CachingStrategy="RecycleElement"> <!--Built in Cells--> <!--<ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding UtilAcctNum}" /> </DataTemplate> </ListView.ItemTemplate>--> <!--Custom View Cells--> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout> <Grid Padding="10"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="45"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Label Text="{Binding UtilAcctNum, StringFormat='Account#: {0}'}" Style="{DynamicResource ListItemTextStyle}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalTextAlignment="Start" TextColor="Default" /> <Label Text="{Binding AmountStr, StringFormat='Amount: ${0:C}'}" Style="{DynamicResource ListItemDetailTextStyle}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" HorizontalTextAlignment="End" TextColor="{Binding DueColor}"/> <Button Text="Pay $" BackgroundColor="Green" TextColor="White" Grid.Row="1" Grid.Column="0" BorderRadius="4" BorderColor="YellowGreen" BorderWidth="2" Command="{Binding LoadPay}"/> <Button Text="Remove -" BackgroundColor="Gray" TextColor="White" Grid.Row="1" Grid.Column="2" BorderRadius="4" BorderColor="LightGray" BorderWidth="2" Command="{Binding Remove}"/> <Button Text="History" BackgroundColor="Green" TextColor="White" Grid.Row="1" Grid.Column="1" BorderRadius="4" BorderColor="YellowGreen" BorderWidth="2" CommandParameter="{Binding UtilAcctNum}" Command="{Binding LoadHistory}"/> </Grid> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button Text="Add +" BackgroundColor="Green" TextColor="White" BorderRadius="4" BorderColor="YellowGreen" BorderWidth="2" Command="{Binding AddUtilAcct}"/> </StackLayout>

    and here is my View Model (some info redacted)

    ``` public class UtilityViewModel : INotifyPropertyChanged { #region UtilityList public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection _items = new ObservableCollection(); public ObservableCollection AccountList = new ObservableCollection(); private MobileUtilityAccountView _selectedItem;

        public Command LoadPay { get; }
        public Command LoadHistory { get; set; }
        public Command Remove { get; }
        public Command AddUtilAcct { get; }
    
        public ObservableCollection<MobileUtilityAccountView> Items
        {
            get
            {
                return _items;
            }
            set
            {
                _items = value;
                OnPropertyChanged("Items");
            }
        }
    
        public MobileUtilityAccountView SelectedItem
        {
            get
            {
                return _selectedItem;
            }
            set
            {
                _selectedItem = value;
                if(_selectedItem == null)
                {
                    return;
                }
                else
                {
                    OnPropertyChanged("SelectedItem");
                }
    
                SelectedItem = null;
    
            }
        }
    
        public UtilityViewModel()
        {
            GetAcctNumbers();
    
            LoadHistory = new Command(HistoryCmd);
        }
    
        private void OnPropertyChanged(string v)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(v));
        }
    
        private async void GetAcctNumbers()
        {
            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Verified.txt");
            var fileContent = File.ReadAllText(path).Split('|');
            string ID = fileContent[0].Replace("\"", "");
            HttpResponseMessage response = new HttpResponseMessage();
            HttpClient client = new HttpClient();
            Uri uri = new Uri("******************);
            response = await client.GetAsync(uri);
            if(response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                Items = JsonConvert.DeserializeObject<ObservableCollection<MobileUtilityAccountView>>(content);
    
                for (var i = 0; i < Items.Count(); i++)
                {
                    if (Items[i].UtilAcctAmtDute > 0)
                    {
                        Items[i].AmountStr = Items[i].UtilAcctAmtDute.ToString();
                        Items[i].DueColor = "#cc0000";
                    }
                    else if (Items[i].UtilAcctAmtDute < 0)
                    {
                        Items[i].AmountStr = Items[i].UtilAcctAmtDute.ToString().Replace("-", "") + " CR";
                        Items[i].DueColor = "#00cc00";
                    }
                    else
                    {
                        Items[i].AmountStr = Items[i].UtilAcctAmtDute.ToString();
                        Items[i].DueColor = "#00cc00";
                    }
                }
            }
        }
    
        private void HistoryCmd(Object obj)
        {
            LoadHistoryPage(obj.ToString());
        }
    
        private async void LoadHistoryPage(string utilAcctNum)
        {
            var page = new NavigationPage(new UtilityAccountDetailsPage(utilAcctNum));
            NavigationPage.SetHasNavigationBar(page, true);
            NavigationPage.SetBackButtonTitle(page, "Back");
            page.Title = "";
            await App.Current.MainPage.Navigation.PushAsync(page, true);
        }
        #endregion
    }
    

    ``` Here is a screenshot of the emulated View:

    Wednesday, August 8, 2018 10:08 PM

Answers

  • User2148 posted

    https://stackoverflow.com/questions/40913439/xamarin-forms-button-command-binding-inside-a-listview

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, August 8, 2018 11:42 PM

All replies

  • User2148 posted

    https://stackoverflow.com/questions/40913439/xamarin-forms-button-command-binding-inside-a-listview

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, August 8, 2018 11:42 PM
  • User76049 posted

    Quick tip,

    The VM should have no idea of the UX consuming layer and just serve up the data (I'd strongly argue this is not a purists view). Also implement INPC in a base viewmodel so you can swap to a framework easier if you choose to do.

    private async void LoadHistoryPage(string utilAcctNum) { var page = new NavigationPage(new UtilityAccountDetailsPage(utilAcctNum)); NavigationPage.SetHasNavigationBar(page, true); NavigationPage.SetBackButtonTitle(page, "Back"); page.Title = ""; await App.Current.MainPage.Navigation.PushAsync(page, true); }

    UX interaction should be abstracted away, Prism is a fab example of how to achieve this, other frameworks are available :)

    Thursday, August 9, 2018 12:06 AM
  • User198446 posted

    You defined commands, but didn't create them

        public Command LoadPay { get; }
        public Command LoadHistory { get; set; }
        public Command Remove { get; }
        public Command AddUtilAcct { get; }
    

    .... public Command loadPay; public Command LoadPay => _loadPay ?? (loadPay = new Command(p => { });

    Thursday, August 9, 2018 12:11 AM