Search code examples
xamarin.formsdata-bindingviewmodel

Xamarin: ViewModel not updating UI


I'm trying to implement ViewModel binding for the first time and am finding that a label that is bound to a property in the viewmodel is not updating to reflect the value I expect it's something silly I'm doing, but would appreciate someone taking a look and helping.

ViewModel:

public class MainPageViewModel : INotifyPropertyChanged
{

    public MainPageViewModel()
    {

    }

    public MainPageViewModel(List<Player> _players, GameType _selectedGame)
    {

        this.Players = _players;
        this.SelectedGame = _selectedGame;
        //Device.BeginInvokeOnMainThread(() =>
        //{
        //    Players = _players;
        //    SelectedGame = _selectedGame;
        //});
        
    }

    private List<Player> players;

    public List<Player> Players
    {
        get { return players; }
        set 
        { 
            players = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Players")); //might need collectionchanged here
        }
    }

    private GameType selectedGame;

    public GameType SelectedGame
    {
        get { return selectedGame; }
        set 
        {
            selectedGame = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedGame"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    
}

GameType:

public class GameType : INotifyPropertyChanged
{
    private string _name = "";
    public string Name
    { 
        get { return _name; } 
        set { _name = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;

    //will have scoring and rules

}

MainPage control:

        <StackLayout x:Name="stackLayoutScoring" Grid.Column="3" Grid.RowSpan="4">
        <Label x:Name="labelGameType" Text="{Binding SelectedGame.Name}" HorizontalTextAlignment="Center" />

MainPageViewModel (with some things I've tried):

public class MainPageViewModel : INotifyPropertyChanged
{

    public MainPageViewModel()
    {

    }

    public MainPageViewModel(List<Player> _players, GameType _selectedGame)
    {

        this.Players = _players;
        this.SelectedGame = _selectedGame;
        //Device.BeginInvokeOnMainThread(() =>
        //{
        //    Players = _players;
        //    SelectedGame = _selectedGame;
        //});
        
    }

    private List<Player> players;

    public List<Player> Players
    {
        get { return players; }
        set 
        { 
            players = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Players")); 
        }
    }

    private GameType selectedGame;

    public GameType SelectedGame
    {
        get { return selectedGame; }
        set 
        {
            selectedGame = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedGame"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    
}

}

GameStartedEventArgs:

public class GameStartedEventArgs : EventArgs, INotifyPropertyChanged
{
    public GameStartedEventArgs(Game game)
    {
        _Game = game;
    }


    private Game _game;
    public Game _Game
    {
        get { return _game; }
        set { _game = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("_Game")); }
    }

    public event PropertyChangedEventHandler PropertyChanged; //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); 
}

MainPage code:

 public void InitGame(Game game)
    {
        //initialize points and setup first player
        stackLayoutScoring.BindingContext = viewModel;
        viewModel.Players = game.Players;
        viewModel.SelectedGame = game.SelectedGame;
        //stackLayoutScoring.BindingContext = viewModel;
    }

Now the viewModel receives the correct values in MainPage.cs, but the labelGameType.Text never updates. I'm not sure if InitGame() getting called from an async method is causing issues:

async void GameStartedAlert(object sender, GameStartedEventArgs e)
    {
        await DisplayAlert("Success!", Statics.CurrentGame.SelectedGame.Name + ": Game Started!", "OK");
        InitGame(e._Game);
        
    }

I'm struggling to spot the issue. I hope it's something daft.

Thanks,

Mike


Solution

  • My problem arose from navigating between pages incorrectly.

    I was using

    async void NavigateToMainPage(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new MainPage());
        }
    

    which creates a new instance of the MainPage, in parallel to the instance that has the databindings. I fixed it by using:

    public void NavigateToMainPage()
        {
            App.Current.MainPage.Navigation.PopAsync();
        }
    

    Thanks to Jason for spotting another issue with my code.