I have two pages: MainPage
and FilterPage
(modal page).
with their respective Viewmodels: MainViewModel
and FilterViewModel
.
In MainPage
I have a listview that's populated with data from an API. The data is passed to the FilterPage
where it is filtered by some specific criteria. In the end a new list is created which is assigned to the binded variable of the listview. What I noticed is that after the modal page closes the listview's items arent updated. What is the proper way to do this?
Model
:
public class Multilist
{
public string Title { get; set; }
public string Date { get; set; }
public string Status { get; set; }
public string Customer { get; set; }
}
MainViewModel
:
public class MainViewModel : INotifyPropertyChanged
{
private IList<Multilist> mainList = new List<Multilist>();
public IList<Multilist> MainList
{
get => mainList;
set
{
if (value == mainList)
return;
mainList = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public MainViewModel(INavigation navigation)
{
this._navigation = navigation;
Task.Run(async () => await GetData());
GotoFilterPageCommand = new AsyncCommand(GotoFilterPage);
}
private async Task GetData()
{
//Gets data from API
MainList = data;
}
private async Task GotoFilterPage()
{
await this._navigation.PushModalAsync(new FilterPage(MainList.ToList()), true);
}
}
FilterViewModel
:
public class FilterViewModel : INotifyPropertyChange
{
public List<Multilist> OldList { get; set; }
private IList<Multilist> mainList = new List<Multilist>();
public IList<Multilist> MainList
{
get => mainList;
set
{
if (value == mainList)
return;
mainList = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public FilterViewModel(List<Multilist> oldlist)
{
Oldlist = oldlist;
SomeCommand = new AsyncCommand(SomeTask);
}
private async Task SomeTask()
{
// Some code here
CreateNewList(OldList);
}
private async Task CreateNewList(List<Multilist> oldlist)
{
//Some code here --> newMainList
pageA.MainList = newMainList;
await App.Current.MainPage.Navigation.PopModalAsync();
}
}
The listview in MainPage
:
<ListView x:Name="TestListView"
ItemsSource="{Binding MainList}"
Grid.Row="4" Grid.ColumnSpan="3"
HasUnevenRows="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="0,0,0,1">
<Grid VerticalOptions="Fill" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding Title}" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="0" />
<Label Text="{Binding Date}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="1" />
<Label Text="{Binding Customer}" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="2" />
<Label Text="{Binding Status}" HorizontalTextAlignment="End" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="3" />
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CodePudding user response:
You could use Singleton to make a global instance for both MainViewModel
and FilterViewModel
.
I make a simple example for your reference.
Model:
public class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
public int Age { get; set; }
}
ViewModel:
public class PersonViewModel
{
#region Singleton Pattern
private PersonViewModel()
{
Persons = new ObservableCollection<Person>()
{
new Person(){ Name="A"},
new Person(){ Name="A2"},
new Person(){ Name="A3"},
new Person(){ Name="A4"},
};
}
public static PersonViewModel Instance { get; } = new PersonViewModel();
#endregion
private ObservableCollection<Person> _person;
public ObservableCollection<Person> Persons
{
get { return _person; }
set { _person = value; }
}
}
Page24: //MainPage
<StackLayout>
<ListView ItemsSource="{Binding MainList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Navigate To FilterPage" Clicked="Button_Clicked">
</Button>
</StackLayout>
Page24 Code behind:
public Page24()
{
InitializeComponent();
this.BindingContext = new Page24ViewModel();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new FilterPage());
}
Page24ViewModel://MainViewModel
public class Page24ViewModel : INotifyPropertyChanged
{
private PersonViewModel _personViewModel;
public Page24ViewModel()
{
_personViewModel = PersonViewModel.Instance;
}
private ObservableCollection<Person> mainList;
public ObservableCollection<Person> MainList
{
get { return _personViewModel.Persons; }
set
{
mainList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
FilterPage:
<ContentPage.Content>
<StackLayout>
<StackLayout>
<Label Text="Name:"></Label>
<Entry x:Name="entry"></Entry>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Button x:Name="btn_Add" Text="Add" Clicked="btn_Add_Clicked"></Button>
<!--<Button x:Name="btn_Delete" Text="Delete" Clicked="btn_Delete_Clicked"></Button>-->
</StackLayout>
<ListView ItemsSource="{Binding MainList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
FilterPage code behind:
private PersonViewModel _personViewModel;
public FilterPage()//modal page
{
InitializeComponent();
this.BindingContext = new FilterViewModel();
}
private void btn_Add_Clicked(object sender, EventArgs e)
{
_personViewModel = PersonViewModel.Instance;
_personViewModel.Persons.Add(new Person() { Name = entry.Text });
}
FilterViewModel:
public class FilterViewModel : INotifyPropertyChanged
{
private PersonViewModel _personViewModel;
public FilterViewModel()
{
_personViewModel = PersonViewModel.Instance;
}
private ObservableCollection<Person> newMainList;
public ObservableCollection<Person> MainList
{
get { return _personViewModel.Persons; }
set
{
newMainList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}