Atm I got a class(somerandomclasss) with a static ObservableCollection:
public static ObservableCollection<PersonViewModel> thepeoplelist = new ObservableCollection<PersonViewModem>();
However, I am converting my project to MVVM and of course this is not a good way to fill all my itemscontrols(listviews mainly) I got multiple windows that use this source in this way.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
lsvallthepeople.ItemSource = somerandomclasss.thepeoplelist;
}
The listview displays then all the people with the info.
However this isn t the MVVM way I bet, and I haven't found a good way to work without a public static ObservableCollection, however there is a window where you got a listview where you can edit the persons, they get updated in the SQL database and in the PersonViewModel (that got the INotifyPropertyChanged).
If you need any more information feel free to ask ofc :).
You can either use a Static
binding to bind your ItemsSource
instead of setting it in your code-behind manually
<ListBox ItemsSource="{Binding Source={
x:Static local:somerandomclasss.thepeoplelist}}" ... />
or expose a property which returns the collection from your ViewModel and bind to that property
public class MyViewModel
{
public ObservableCollection<PersonViewModel> PersonList
{
get { return somerandomclasss.thepeoplelist; }
}
...
}
<ListBox ItemsSource="{Binding PersonList}" ... />
I believe what your are asking is, "What is the best way to share a list of items across multiple views in MVVM?" so I'll answer the question that way.
Let's assume you have a service that provides a way to get the list of people. You called it a "PersonViewModel", but you might be confusing a domain entity with a ViewModel. In MVVM, you have a view which represents the UI control or screen. Then you have a ViewModel which binds to the view and joins the View to the data/domain model. The ViewModel can have a number of public properties that the View will bind to including methods that call services to populate those properties from your data model. In your case, I would have a View + ViewModel and the ViewModel has an ObservableCollection property of class "Person".
The following code is a paraphrase of what it might actually look like. Not everything is implemented.
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<Person> People { get; set; }
public MyViewModel()
{
this.People = new ObservableCollection<Person>();
this.LoadPeople();
}
private void LoadPeople()
{
this.People.Clear();
// Have your logic here that loads the people collection
}
}
As far as managing a single list, I recommend caching the people collection in some kind of static class or singleton that is accessible to your view models. Then the LoadPeople method above can just pull from the cache. You can even lazy-load the cache so that it doesn't make the service request until its first access.
internal static class SystemContext
{
private static ObservableCollection<Person> _people = null;
public static ObservableCollection<Person> People
{
get
{
if( _people == null )
{
_people = new ObservableCollection<Person>();
// load the list here from a service or something
}
return _people;
}
}
}
So in your ViewModel, LoadPeople would then look something like this:
public void LoadPeople()
{
this.People.Clear();
foreach( Person person in SystemContext.People )
{
this.People.Add( person );
}
}
Your UI will then look something like this:
<ListBox
ItemsSource={Binding Path=People, Mode=OneWay}
SelectedItem={Binding Path=SelectedPerson, Mode=TwoWay}
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=PersonName, Mode=OneWay}" />
<TextBlock Text="{Binding Path=DateOfBirth, Mode=OneWay}" />
<!-- etc -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should probably have this as a service or repository that is injected into each view model (as an abstraction) that requires the list.
As you want to populate this list just once, you could either do this in the constructor of the service/repository implementation or use a caching technique in the services GetList
method.
As you always want to pass the same instance to each view model, you should use either a singleton pattern, or register the abstraction (e.g. IPeopleRepository
) against a particular instance if using an IoC container.
As this would be a service/repository, you should return a type that isn't dependent on the presentation technology, i.e. an IEnumerable<T>
or IList<T>
, and use this to create your observable collections in each view model.