Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin List View SelectedItem ViewModel binding

Tags:

c#

xamarin

Using Visual Studio 2015 proff,

My LoginViewModel Class (Portable Class Library)

public class LoginViewModel : INotifyPropertyChanged, INotifyCollectionChanged
{
    LoginPage page;
    private ObservableCollection<Employees> _employeeList;
    private string _loginName;

    public ObservableCollection<Employees> EmployeeList
    {
        get { return _employeeList; }
        set
        {
            _employeeList = value;
            OnPropertyChanged();
            OnCollectionChanged(NotifyCollectionChangedAction.Reset);
        }
    }

    public string LoginName
    {
        get { return _loginName; }
        set
        {
            _loginName = value;
            if (_loginName != null)
            {
                OnPropertyChanged();
            }
        }
    }

    public LoginViewModel(LoginPage parent)
    {
        page = parent;
    }

    public async void GetEmployees()
    {
        var loginService = new LoginService();
        EmployeeList = await loginService.GetEmployeesAsync();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    protected virtual void OnCollectionChanged( NotifyCollectionChangedAction action)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action));
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    }
}

My LoginPage.xaml (Portable Class Library)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="ScannerApp.Views.LoginPage"
         xmlns:ViewModels="clr-namespace:ScannerApp.ViewModels;assembly=ScannerApp">
<StackLayout Orientation="Vertical">

<Label Text="Please Login"
        VerticalOptions="Start"
        HorizontalTextAlignment="Center"
        IsVisible="true"
        FontSize="Large"
        FontAttributes="Bold" />

<ListView x:Name="mylist" ItemsSource="{Binding EmployeeList}"
        HasUnevenRows="True" SelectedItem="{Binding LoginName}">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <StackLayout Orientation="Vertical" Padding="12,6">
          <Label Text="{Binding Name}"/>
        </StackLayout>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

My LoginPage.xaml.cs Class (Portable Class Library)

public partial class LoginPage : ContentPage
{
    public LoginPage()
    {
        InitializeComponent();
        BindingContext = new LoginViewModel(this);
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        LoginViewModel model = new LoginViewModel(this);
        model.GetEmployees();
        BindingContext = model;
    }

    public ListView MyList
    {
        get
        {
            return mylist;
        }
    }
}

Question

I get a list of employees, the list on the front end renders this. The user then Selects a name from the list, At this point I would like to detect this and then navigate to my different page. Currently my property is not hit, I'm wondering if this has anything to do with my Binding on the code behind "OnAppearing"? but I'm not sure.

like image 910
lemunk Avatar asked Sep 02 '25 05:09

lemunk


1 Answers

While what you have may work there are a few tweaks I would suggest.

No need to set your BindingContext in your constructor and in OnAppearing(). Just make your LoginViewModel a class level private property in your code-behind and only assign it to your BindingContext in your constructor. Then call GetEmployees() in OnAppearing().

Also, you should make GetEmployees() return a Task, in order to await as far up the chain as possible.

ViewModel:

....

public async Task GetEmployees()
    {
        var loginService = new LoginService();
        EmployeeList = await loginService.GetEmployeesAsync();
    }

....

Code-behind:

public partial class LoginPage : ContentPage
{
    private LoginViewModel _model;

    public LoginPage()
    {
        InitializeComponent();
        BindingContext = _model = new LoginViewModel(this);
    }

    protected override async void OnAppearing() //Notice I changed this to async and can now await the GetEmployees() call
    {
        base.OnAppearing();
        await _model.GetEmployees();
    }

    public ListView MyList
    {
        get
        {
            return mylist;
        }
    }

    private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e) {
        if (e.SelectedItem == null) return; 

        await Navigation.PushAsync(new MenuPage());
    }
}

XAML:

<!-- Adding OnItemSelected in XAML below -->
<ListView x:Name="mylist"
          ItemsSource="{Binding EmployeeList}"
          HasUnevenRows="True"
          SelectedItem="{Binding LoginName}"
          ItemSelected="OnItemSelected">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <StackLayout Orientation="Vertical" Padding="12,6">
          <Label Text="{Binding Name}"/>
        </StackLayout>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>
like image 139
hvaughan3 Avatar answered Sep 04 '25 22:09

hvaughan3