views:

83

answers:

4

Hello,

I have the following simplified ViewModel

public class UserViewModel : IUserViewModel
{
    public DelegateCommand<object> NewUser { get; private set; }

    public ObservableCollection<UserInfo> UserList { get; set; }

    public UserViewModel( ) {
        NewUser = new DelegateCommand<object>( OnNewUser );

        this.UserList = new ObservableCollection<UserInfo>( );
        UserList.Add( new UserInfo( "Peter" );
        UserList.Add( new UserInfo( "Paul" );
        UserList.Add( new UserInfo( "Mary" );
    }

    private void OnNewUser( object parameter ) {
        UserInfo user = new UserInfo( "User"+DateTime.Now.ToLongTimeString(), 0, true, false, false );
        UserList.Add( user );
    }
}

This view model is the data context for a (simplified) view:

public partial class UserView : ViewBase
{
    public UserView( UserViewModel viewModel ) {
    InitializeComponent( );

    this.DataContext = viewModel;
    }
}

The view itself contains a ListBox that is bound to the UserList of the UserViewModel:

<ListBox ItemsSource="{Binding UserList}" DisplayMemberPath="UserID" />

When the view model is initialized it adds a user to the list. This user shows up in the list box. When afterwards the NewCommand is invoked, new users get added to the list. But the list box does not update with the new users.

I added a CollectionChanged handler to the view model as well as to the view. The views handler was added inside the constructor like this:

viewModel.UserList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( UserList_CollectionChanged );

[EDIT]

The command to add a new user to the collection is triggered by a button from a different view:

public class UserButtonBarViewModel : IUserButtonBarViewModel
{

    public UserButtonBarViewModel( IUserButtonBarView view, IUserViewModel mainModel ) {
        this.View = view;
        NewUser = mainModel.NewUser;
        this.View.Model = this;
    }

    public ICommand NewUser { get; private set; }

    public IUserButtonBarView View {get; private set; }
}

The UserButtonBarView looks like this:

<UserControl ...>
    <Button Command="{Binding NewUser}" Content="New user"/>
</UserControl>

And the view's code behind:

public partial class UserButtonBarView : IUserButtonBarView
{

    public UserButtonBarView( ) {
        InitializeComponent( );
    }

    public IUserButtonBarViewModel Model {
        get {
            return DataContext as IUserButtonBarViewModel;
        }
        set {
            DataContext = value;
        }
    }
}

[/EDIT]

The interesting point is, that the handler in the view model was executed when a new user was added to the collection, but the view's handler was not invoked.

Why gets the view's handler not invoked as well? And why is the list box not updating itself when the collection in the view model is changed?

A: 

Can you post the code that actually adds the new user? It seems like it might happen on a different thread than the UI? Try using Dispatcher.Invoke...

rudigrobler
should be a comment
Natrium
I moved the question to a comment... sorry
rudigrobler
A: 

I think you need to use TwoWay binding.

<ListBox ItemsSource="{Binding UserList, Mode=TwoWay}" DisplayMemberPath="UserID" />
JSprang
Sorry, but this doesn't do the trick. Do I have to have the view model imlement INotifyPropertyChanged for the TwoWay binding?
PVitt
I just created myself a VERY simple test application and it seemed to work without the two-way binding. I didn't use a 'Command' like you did (I just added something in the codebehind), but that shouldn't matter. I could post the code if you think it would be beneficial.
JSprang
Just noticed your EDIT about the different view. That obviously has something to do with your issue, so nothing I have posted will help, sorry!
JSprang
TwoWay doesn't do anything when set on the ItemsSource property since the ItemsControls don't change the collections bound to them. For TwoWay to have any effect the ListBox would have to internally be assigning a new collection instance to its ItemsSource property which would then be pushed to the VM and replace the entire collection instance that had previously been assigned to UserList. This is obviously not desirable, which is why it doesn't work that way.
John Bowen
+3  A: 

It sounds like you are creating a second copy of the ViewModel somewhere (probably in your ButtonViewModel) and your NewUser button is adding a user to one copy while the ListBox remains bound to another copy.

Can you post your NewUser command code?

Rachel
The code of the NewUser command is already posted in the first code paragraph. The NewUser command ia assigned a new delegate command that point to OnNewUser(). This method simply creates a new instance of UserInfo and adds it to the colletion.
PVitt
The dependecies get resolved via a IoC container. The container has injected another instance of the UserViewModel into the UserButtonBarViewModel. After adding a ContainerControlledLifetimeManager to the UserViewModel, it works. Thanks for your help.
PVitt
A: 

I saw that your UserList property has a setter - when you make changes to the user list are you clearing and adding to the current ObservableCollection, or creating a new one?

Gary Evans
I have to apologize. The setter isn't used and I have just forgotten to delete it. Sorry, but thank you anyway.
PVitt