How can I replace static ObservableCollection so i

2019-01-29 05:09发布

问题:

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 :).

回答1:

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}" ... />


回答2:

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>


回答3:

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.