Binding to a method?

Oct 6, 2010 at 4:27 PM

Hello all,

I have a VM which exposes a collection of items. These items are displayed in a ListView. In this listview, I have a checkbox. The Checked event is bound to a method, which adds or remove the item from a second list.

How can I bind the IsChecked property of the checkbox to the existence of the item in the second list?

Thanks in advance

Mike

Oct 6, 2010 at 9:22 PM

I suggest you to create a second (observable) collection containing only selected items; this collection will be bound to a second listview.
In the action code, you can add or remove the object associated to the interested checkbox from the second list, thus indirectly updating the UI. 

Oct 6, 2010 at 9:46 PM

I  realized that my advice is incomplete, because if the second collection is changed, the UI is not updated. This is another approach I often use:

public interface IWhateverItem {
...

	string DisplayName {get;}
...
}

public class SelectableWhateverItem : PropertyChangedBase {
	IWhateverItem _inner;
	ICollection<IWhateverItem> _selectedCollection;
	public SelectableWhateverItem(IWhateverItem inner, ICollection<IWhateverItem> selectedCollection) {
		_inner = inner;
		_selectedCollection = selectedCollection;
		
		var notifier = _selectedCollection as INotifyCollectionChanged;
		if (notifier!= null) 
			notifier.CollectionChanged += (o,e)=> { NotifyOfPropertyChange("IsSelected");};
	}

	public string DisplayName {
		get {return _inner.DisplayName; }
	}
	
	public bool IsSelected {
		get {return _selectedCollection.Contains(_inner); }
		set {
			if (value) 
				_selectedCollection.Add(_inner); 
			else 
				_selectedCollection.Remove(_inner); 
			
			NotifyOfPropertyChange("IsSelected");
		}
	}
}

public class SomeSelectionVM {

	public SomeSelectionVM (...) {
		IEnumerable<IWhateverItem> availableItems = ... //call some service?
		
		SelectedItems = new  BindableCollection<IWhateverItem>(); 	
		AvailableItems = new  BindableCollection<IWhateverItem>(
			availableItems.Select(x=>new SelectableWhateverItem(x, SelectedItems);
		); 
	}
...	
	

	public BindableCollection<SelectableWhateverItem> AvailableItems {get; set;}
	public BindableCollection<IWhateverItem> SelectedItems {get; set;}
	
...
}

 

<ListBox ItemsSource="{Binding AvailableItems}">
	<ListBox.ItemTemplate>
		<CheckBox IsChecked={Binding IsSelected} Content="{Binding DisplayName}"/>
	</ListBox.ItemTemplate>
</ListBox>

<ListBox ItemsSource="{Binding SelectedItems}" DisplayMemberPath="DisplayName" />

Oct 7, 2010 at 9:14 AM

I must not have been clear enough in the description of my problem, but I only want one ListView, but two lists in my view model: the first list containing all the items, whereas the second containing only the selected items.

But I'm wondering if I can't make this work using a converter. I'll try, and post my result here.

Oct 7, 2010 at 10:53 AM

My solution should do the job anyway, you just don't need to bind the second collection. It may seem a lot of code, but it's just a simple VM (SelectableWhateverItem) wrapping your IWhateverItem domain object. Plus, you can easily make a generic version supporting similar scenarios.

You can probably achieve the same with a converter, if you really want, but I can't see a simple way to take into account the current item and the whole SelectedItems collection (maybe with a MultipleValueConverter). I usually avoid complex tricks in the UI if there is a chance to solve the problem at the VM side.

The choice of a solution greatly depends on the exact requirents: do you care working on an instance collection exposed by the VM or a newly created collection will be ok? Do you want the UI to react to changes in the SelectedCollection?

SelectableWhateverItem 
Oct 7, 2010 at 12:19 PM

I've investigated your answer, and it works great! Thanks

But I have a question anyways :)

Doesn't BindableCollection fire an event when it is modified?

I have to write this code:

private readonly BindableCollection<PlayerInTeam> selectedPlayers = new BindableCollection<PlayerInTeam>();

protected NewIndexationWizardRosterBase(ISession session)
    : base(session)
{
    selectedPlayers.CollectionChanged += (o, e) => NotifyOfPropertyChange(() => SelectedPlayers);
}

public BindableCollection<PlayerInTeam> SelectedPlayers { get { return selectedPlayers; } }

I f I want to be warned about SelectedPlayers modifications. (my VM implements IDataErrorInfo, and I've put in place some validations mechanisms which don't work if I comment the line in the VM's ctor.

 

Coordinator
Oct 7, 2010 at 1:14 PM

Remember that there's a difference in the SelectedPlayers property changing vs. the contents of the SelectedPalyers collection changing. Because you care about changes in the collection items, you must wire the event.