views:

287

answers:

3

Hi there

In WPF I have a collection of bool? values and I want to bind each of these to a separate checkbox programmatically. I want the bindings to be TwoWay so that changing the value of the individual item in the collection in code updates the check box and vice versa.

I have spent ages trying to figure out how to do this and I am completely stuck. With the following code the checkbox only gets the right value when the window is loaded and that's it. Changing the check box doesn't even update the value in the collection. (UPDATE: this appears to be a bug in .NET4 as the collection does get updated in an identical .NET3.5 project. UPDATE: Microsoft have confirmed the bug and that it will be fixed in the .NET4 release.)

Many thanks in advance for your help!

C#:

namespace MyNamespace
{
    public partial class MyWindow : Window, INotifyPropertyChanged
    {
        public MyWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
               PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }

        public List<bool?> myCollection = new List<bool?>
            { true, false, true, false, true, false };

        public List<bool?> MyCollection
        {
            get { return myCollection; }
            set { myCollection = value; }
        }
    }
}

XAML:

<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}">
A: 

What makes you think it's not working? It's working for me :)

Here's my test XAML:

<UniformGrid>
    <CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}"/>
    <ListBox ItemsSource="{Binding MyCollection}"/>
    <Button Content="Test" Click="Button_Click"/>
</UniformGrid>

Here's my code behind:

private void Button_Click(object sender, RoutedEventArgs e)
{

}

(the rest is the same as yours)

I placed a breakpoint on Button_Click and checked MyCollection[0] it was updated according to the IsChecked value of the CheckBox.

Try changing your collection type from List<bool?> to ObservableCollection<bool?> perhaps that is the reason you think it's not working for you (the fact that changes to the collection are not reflected anywhere else in your view).

Aviad P.
Thanks for your response Aviad. I tried my code again in a new project just to be sure but no luck. Not sure how you did it! However, when I changed the List<> to ObservableCollection<>, changes to the value in code were instantly reflected on the checkbox - but not vice versa. But that's a start at least.
Matt Jenkins
See my comment above that CheckBox->MyCollection works in .NET3.5 but not .NET4
Matt Jenkins
+1  A: 

Change your List<bool?> to an ObservableCollection<bool?>. A List does not raise the change notifications that WPF needs to update the UI. An ObservableCollection does. This handles the case where the list entry is changed and the CheckBox needs to update accordingly.

In the other direction, it works for me even with a List<bool?> -- i.e. toggling the checkbox modifies the value in the collection. Your binding syntax is certainly correct.

itowlson
Thanks for your response. Changing to an ObservableCollection does cause the UI to be updated when the values change in code. But bizarrely the other direction does not work with the rest of my code left as-is.
Matt Jenkins
See my comment above that CheckBox->MyCollection works in .NET3.5 but not .NET4
Matt Jenkins
+2  A: 

Hi,

There are a few things that need changing here to get this to work. Firstly you'll need to wrap your boolean value in an object that implements the INotifyPropertyChanged interface in order to get the change notification that you are looking for. Currently you are binding to boolean values in your collection which do not implement the interface. To do this you could create a wrapper class like so :

  public class Wrapper: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private bool val = false;

        public bool Val
        {
            get { return val; }
            set
            {
                val = value;
                this.OnPropertyChanged("Val");
            }
        }

        public Wrapper(bool val)
        {
            this.val = val;
        }

    }

You'll then want to create these objects in your form instead of a list of booleans. You may also want to use an observable collection instead of a list so that notification of items being added and removed are sent. This is shown below:

public Window1()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private ObservableCollection<Wrapper> myCollection = new ObservableCollection<Wrapper>()
        {new Wrapper(true), new Wrapper(false), new Wrapper(true)};


    public ObservableCollection<Wrapper> MyCollection
    {
        get { return myCollection; }
    }

The next thing to do is to display a list of check boxes in your ui. To do this WPF provides itemscontrols. ListBox is an itemscontrol so we can use this as a starting point. Set the itemssource of a listbox to be MyCollection. We then need to define how each Wrapper object is going to be displayed in the list box and this can be done with a datatemplate which is created in the windows resources. This is shown below :

<Window.Resources>
    <DataTemplate x:Key="myCollectionItems">
        <CheckBox IsChecked="{Binding Path=Val, Mode=TwoWay}"></CheckBox>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListBox ItemsSource="{Binding Path=MyCollection}" ItemTemplate="{StaticResource myCollectionItems}"></ListBox>
</Grid>

This should get you up and running with a simple demo of checkboxes that have values bound to a list of booleans.

mattythomas2000
-1 Though your suggestions may be valid, there's only one thing required for the original poster's code to work, changing the collection type from `List` to `ObservableCollection` - Your answer may be right, but not to the question at hand.
Aviad P.
The question states that there is a requirement to bind to a list of checkboxes, this is clearly better done using an itemscontrol than binding to each item. The question also states that changes in code should cause the checkbox to change, which was demonstrated by implementing the change notification in the wrapper object. As far as I'm aware changes won't be passed to the UI without doing this.
mattythomas2000
Thank you very much for this Matt - this approach will definitely be useful as I intended to create the checkboxes programmatically and your answer provides an elegant solution for that.
Matt Jenkins