Reputation: 3
So I'm creating my first app using Xamarin Forms. I initially had some trouble getting my views to update with my bindings but I received some great help from the people on here and was able to grasp the concept. Now I'm trying to learn how to implement ListViews into my app. I'm running into the exact same problem as before, my Listview isn't updating when I click the button to advance a game day. The day value in my stacklayout on the same page updates fine and my bindings and everything are written the same way as the other pages and data I have that update but still nothing happens to the ListView. Also I am using an observable collection which I know is supposed to update the view when an item is changed, added or deleted. The data does however update if I open my menu and navigate to a new instance of the page so I know the button and everything else is working fine it's just the view that isn't updating. I'm sure it is probably something silly that I'm not seeing, as it was with my other issues but any help would be great. I've been stuck starring at the problem for sometime now and have yet to find solution on the internet. Thanks in advance!
Here is my DrugInventoryPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 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"
xmlns:local="clr-namespace:KingPinMobile.ViewModels"
mc:Ignorable="d"
x:Class="KingPinMobile.Views.DrugInventoryPage"
Title="Inventory">
<ContentPage.BindingContext>
<local:PlayerInventoryViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="+24hrs" Command="{Binding PIVMOn1Turn}" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout>
<StackLayout Orientation="Horizontal" Padding="20">
<Label Text="Money:" HorizontalOptions="Start"/>
<Label x:Name="lblPlayerMoney" Text="{Binding Money, Mode=OneWay}" HorizontalOptions="FillAndExpand"/>
</StackLayout>
<StackLayout Padding="0">
<BoxView HeightRequest="1" HorizontalOptions="FillAndExpand" Color="Gray" />
</StackLayout>
<ListView x:Name="lvPlayerDrugs" ItemsSource="{Binding PlayerDrugs}">
<ListView.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="Drug" Grid.Row="0" Grid.Column="0" HorizontalOptions="CenterAndExpand" BackgroundColor="LightGray"/>
<Label Text="Quantity" Grid.Row="0" Grid.Column="1" HorizontalOptions="CenterAndExpand" BackgroundColor="LightGray"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" Grid.Row="0" Grid.Column="0"/>
<Label Text="{Binding Quantity}" Grid.Row="0" Grid.Column="1"/>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Here is the DrugInventoryPage.xaml.cs:
using KingPinMobile.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace KingPinMobile.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DrugInventoryPage : ContentPage
{
public DrugInventoryPage()
{
InitializeComponent();
}
}
}
Here is the PlayerInventoryViewModel.cs:
using Engine;
using KingPinMobile.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace KingPinMobile.ViewModels
{
public class PlayerInventoryViewModel : HomePageViewModel
{
public PlayerInventoryModel model = new PlayerInventoryModel();
public ObservableCollection<DrugInventoryItem> Player1Drugs = new ObservableCollection<DrugInventoryItem>();
public PlayerInventoryViewModel()
{
PIVMOn1Turn = new Command(execute: PIVMOn1Turns);
Player1Drugs = model.PlayerDrugs;
}
public ICommand PIVMOn1Turn { get; private set; }
public ObservableCollection<DrugInventoryItem> PlayerDrugs
{
get { return Player1Drugs; }
set { Player1Drugs = value; OnPropertyChanged(); }
}
public void PIVMOn1Turns()
{
_model.On1Turn();
_model.refreshModel();
model.refreshPlayerInventoryModel();
CurrentDay = Math.Floor(ProgressionDirectory.CurrentGameDay.DayNumber).ToString();
Money = Convert.ToInt32(Math.Floor(World._player.PlayerMoney)).ToString();
PlayerDrugs = World._player.DrugInventory;
Player1Drugs = World._player.DrugInventory;
}
}
}
Here is the HomePageViewModel.cs the previous ViewModel inherits from:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using Engine;
using Hangfire.Annotations;
using Xamarin.Forms;
namespace KingPinMobile
{
public class HomePageViewModel : INotifyPropertyChanged
{
public static HomeModel _model = new HomeModel();
public Player _player = _model.Player1;
public string Day = _model.DayText;
public string MoneyValue = _model.MoneyText;
public string CurrentLocation = _model.Player1.CurrentLocation.Name;
public HomePageViewModel()
{
OnTurn = new Command(execute: On1Turn);
}
public ICommand OnTurn { get; private set; }
public string CurrentDay
{
get { return Day; }
set { Day = value; OnPropertyChanged(); }
}
public string Money
{
get { return MoneyValue; }
set { MoneyValue = value; OnPropertyChanged(); }
}
public string PlayerLocation
{
get { return CurrentLocation; }
set { CurrentLocation = value; OnPropertyChanged(); }
}
public void On1Turn()
{
_model.On1Turn();
_model.refreshModel();
CurrentDay = Math.Floor(ProgressionDirectory.CurrentGameDay.DayNumber).ToString();
Money = Convert.ToInt32(Math.Floor(World._player.PlayerMoney)).ToString();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is the PlayerInventoryModel.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
public class PlayerInventoryModel : HomeModel
{
public ObservableCollection<DrugInventoryItem> PlayerDrugs = World._player.DrugInventory;
public void refreshPlayerInventoryModel()
{
PlayerDrugs = World._player.DrugInventory;
refreshModel();
}
}
}
Finally the HomeModel.cs that the previous Model inherits from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine;
namespace Engine
{
public class HomeModel
{
public string DayText = Math.Floor(ProgressionDirectory.CurrentGameDay.DayNumber).ToString();
//public decimal Money = Convert.ToInt32(Math.Floor(World._player.PlayerMoney)).;
public string MoneyText = Convert.ToInt32(Math.Floor(World._player.PlayerMoney)).ToString();
public Day CurrentDay = ProgressionDirectory.CurrentGameDay;
public Player Player1 = World._player;
public string TrueDay = ProgressionDirectory.CurrentGameDay.DayNumber.ToString();
public void On1Turn()
{
Day CurrentGameDay = ProgressionDirectory.CurrentGameDay;
Advance1DAY.Advance1DAYS(CurrentGameDay);
decimal TurnsPassed = 1;
Sell.DrugsOnTurn(TurnsPassed);
ActionDirectory.OnTurn(TurnsPassed);
}
public void refreshModel()
{
DayText = Math.Floor(ProgressionDirectory.CurrentGameDay.DayNumber).ToString();
MoneyText = Convert.ToInt32(Math.Floor(World._player.PlayerMoney)).ToString();
CurrentDay = ProgressionDirectory.CurrentGameDay;
Player1 = World._player;
TrueDay = ProgressionDirectory.CurrentGameDay.DayNumber.ToString();
}
}
}
Upvotes: 0
Views: 976
Reputation: 1084
I don't really get why you got this implementation instead of having backing fields for your properties and raise property changed when you want another property to be aware of the changes, but anyway, you return or set a Player1Drugs
collection. Btw, why do you set Player1Drugs
here in the setter ?
public ObservableCollection<DrugInventoryItem> PlayerDrugs
{
get { return Player1Drugs; }
set { Player1Drugs = value; OnPropertyChanged(); }
}
However, as I told you, maybe you should do some refactoring on your properties, thus your PlayerDrugs
can be aware of any changes on Player1Drugs
.
public ObservableCollection<DrugInventoryItem> PlayerDrugs => Players1Drugs;
private ObservableCollection<DrugInventoryItem> _player1Drugs;
public ObservableCollection<DrugInventoryItem> Player1Drugs
{
get { return _player1Drugs; }
set {
if(_player1Drugs == value) return;
_player1Drugs = value;
OnPropertyChanged();
OnPropertyChanged("PlayerDrugs")
}
}
Or, doing two properties with backing fields (like the one just above for Player1Drugs
), and re-arrange your code logic.
EDIT : Ran your sample and got it working. If you want to trigger the property changes, you should create a new ObservableCollection
in your PIVMon1Turns()
:
Player1Drugs = new ObservableCollection<DrugInventoryItem>(World.Player1.DrugInventory);
Upvotes: 0
Reputation: 1620
Invoke Property as Send property Name
OnPropertyChanged("PlayerLocation");
Upvotes: 0