简体   繁体   中英

How to bind an ObservableCollection<bool> to a Listbox of Checkboxes in WPF

Let me prefix this question by stating that I'm very new to both C# and WPF.

I'm trying to connect a collection of Boolean values to a container containing 6 checkboxes, and have the state of these values stored when a button is pressed. I'm assuming there is an easy way to do this, since binding checkboxes to a collection of seems a very natural thing to do, but all the solutions I have seen so far have seemed overly complicated (example: http://merill.net/2009/10/wpf-checked-listbox/ ).

I create the checkboxes by modifying the data template of a ListBox and set the ItemsSource of the ListBox to the ObservableCollection , but my problem is that I don't know what to bind the IsChecked to, since I'm trying to bind it to the actual object in the collection and not a property of the object.

Use IsChecked="{Binding}" to bind the item of the collection directly.

<ListBox ItemsSource="{Binding MyBooleanCollection}" >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Mode=OneWay}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

However it is not possible to do bind to source with this method. Because the binding on the IsChecked property of the CheckBox doesn't the index of the item of the binding. So, it can't change the collection but only the item of the collection.

Update

To get around that limitation, you can create a wrapper to the Boolean value:

public class Wrapper<T> : INotifyPropertyChanged
{
    private T value;
    public T Value
    {
        get { return value; }
        set
        {
            {
                this.value = value;
                OnPropertyChanged();
            }
        }
    }

    public static implicit operator Wrapper<T>(T value)
    {
        return new Wrapper<T> { value = value };
    }
    public static implicit operator T(Wrapper<T> wrapper)
    {
        return wrapper.value;
    }

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

Here is an exemple of usage:

public partial class MainWindow : Window
{
    public ObservableCollection<Wrapper<bool>> MyBooleanCollection { get; private set; }

    public MainWindow()
    {
        InitializeComponent();

        DataContext = this;
        MyBooleanCollection = new ObservableCollection<Wrapper<bool>>() { false, true, true, false, true };
    }
}

And in the XAML:

<CheckBox IsChecked="{Binding Value}"/>

In the ItemTemplate you may write

<CheckBox IsChecked="{Binding Path=.}"/>

or

<CheckBox IsChecked="{Binding Mode=OwnWay}"/>

which binds directly to the item object, ie the bool value from the collection.


Note however that in either case the bound values in the collection will not be replaced when a CheckBox is checked or unchecked. In order to immediately update the collection when a CheckBox is clicked, your collection would have to contain objects with a boolean property to which IsChecked is bound.

In your case this could be as simple as the following (as your question sounds like you don't need property change notifications):

public class BooleanHelper
{
    public bool Value { get; set; }
}

The binding would now look like this:

<CheckBox IsChecked="{Binding Path=Value}"/>

The collection would now be an ObservableCollection<BooleanHelper> and you would perhaps add items like this:

Items.Add(new BooleanHelper { Value = true });

By the link that you provided, you could also use INotifyPropertyChanged extension to your CheckedListItem class, but thats if you dont want use ObservableCollection. It would be something like this:

public class CheckedListItem : INotifyPropertyChanged
{
    private int _Id;
    public int Id 
    {
        get;
        set; NotifyIfAnythingChanged("Id");
    }

    private string _Name;
    public string Name
    {
        get;
        set; NotifyIfAnythingChanged("Name");
    }

    private bool _IsChecked;
    public bool IsChecked
    {
        get;
        set; NotifyIfAnythingChanged("IsChecked");
    }

    private void NotifyIfAnythingChanged(String propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

Your listbox should be something like this:

<ListBox x:Name="MyListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Content={Binding Path=Name} IsChecked="{Binding Mode=TwoWay, Path=IsChecked}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

In your code behind you should initialize the ObservableCollection just once, because every change made into it will result into an UI update.

ObservableCollection<CheckedListItem> MyList = new ObservableCollection<CheckedListItem>();
MyListBox.ItemsSource = MyList;

Now every change made into MyList, such as Add(), Remove(), Etc. Will affect your UI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM