简体   繁体   中英

How to bind an object's boolean property to a CheckBox's IsChecked property?

I have an ObservableCollection of objects that have a boolean property.

In the GUI, I have a CheckBox from which I want to bind its IsChecked property to each object's boolean property.

Is it possible using XAML only? How?

I want to do it only with binding cause biding is faster than loop

Try this:

<ListBox ItemsSource={Binding path}>
  <ListBox.ItemTemplate>
    <DataTemplate>
      <CheckBox IsChecked="{Binding yourBoolPropertyName, Mode = TwoWay}" />
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

This will create list of checkboxes that will bind to your collection. Of course, you must properly set paths for binding.

Create a bool property on your ViewModel which will loop through all Objects of your ObservableCollection to see property is true for every object -

public bool AllTrue
{
   get
   {
      return Objects.All(o => o.Selected);
   }
}

Here Objects is instance of your ObservableCollection and Selected is a bool property in an object.

XAML

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

I have created a behavior to allow a property in a control to be bound to a property of a collection of items, in a way that:

  • If you change the property in the control, all of the items are updated.
  • If you change the property in a item, if all the items have the same property the control will reflect it. If not, the property of the control will be given a fallback value (like null).

     public class CollectionPropertyBehavior : Behavior<DependencyObject> { private IEnumerable<ValueProxy> proxies; private bool syncking; public string SourcePropertyPath { get { return (string)GetValue(SourcePropertyPathProperty); } set { SetValue(SourcePropertyPathProperty, value); } } public static readonly DependencyProperty SourcePropertyPathProperty = DependencyProperty.Register("SourcePropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); public string CollectionPropertyPath { get { return (string)GetValue(CollectionPropertyPathProperty); } set { SetValue(CollectionPropertyPathProperty, value); } } public static readonly DependencyProperty CollectionPropertyPathProperty = DependencyProperty.Register("CollectionPropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); private IEnumerable<object> Items { get { return this.ItemsSource == null ? null : this.ItemsSource.OfType<object>(); } } public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ItemsSourceChanged)); private object Value { get { return (object)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ValueChanged)); public object DefaultValue { get { return (object)GetValue(DefaultValueProperty); } set { SetValue(DefaultValueProperty, value); } } public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.Register("DefaultValue", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); private static void ValueChanged(object sender, DependencyPropertyChangedEventArgs args) { var element = sender as CollectionPropertyBehavior; if (element == null || element.ItemsSource == null) return; element.UpdateCollection(); } private static void ItemsSourceChanged(object sender, DependencyPropertyChangedEventArgs args) { var element = sender as CollectionPropertyBehavior; if (element == null || element.ItemsSource == null) return; element.ItemsSourceChanged(); } private void ItemsSourceChanged() { this.proxies = null; if (this.Items == null || !this.Items.Any() || this.CollectionPropertyPath == null) return; // Cria os proxies this.proxies = this.Items.Select(o => { var proxy = new ValueProxy(); proxy.Bind(o, this.CollectionPropertyPath); proxy.ValueChanged += (s, e) => this.UpdateSource(); return proxy; }).ToArray(); this.UpdateSource(); } private void UpdateSource() { if (this.syncking) return; // Atualiza o valor using (new SynckingScope(this)) { object value = this.proxies.First().Value; foreach (var proxy in this.proxies.Skip(1)) { value = object.Equals(proxy.Value, value) ? value : this.DefaultValue; } this.Value = value; } } private void UpdateCollection() { // Se o valor estiver mudando em função da atualização de algum // elemento da coleção, não faz nada if (this.syncking) return; using (new SynckingScope(this)) { // Atualiza todos os elementos da coleção, // atrávés dos proxies if (this.proxies != null) foreach (var proxy in this.proxies) proxy.Value = this.Value; } } protected override void OnAttached() { base.OnAttached(); // Bind da propriedade do objeto fonte para o behavior var binding = new Binding(this.SourcePropertyPath); binding.Source = this.AssociatedObject; binding.Mode = BindingMode.TwoWay; BindingOperations.SetBinding(this, ValueProperty, binding); } protected override void OnDetaching() { base.OnDetaching(); // Limpa o binding de value para a propriedade do objeto associado this.ClearValue(ValueProperty); } internal class SynckingScope : IDisposable { private readonly CollectionPropertyBehavior parent; public SynckingScope(CollectionPropertyBehavior parent) { this.parent = parent; this.parent.syncking = true; } public void Dispose() { this.parent.syncking = false; } } internal class ValueProxy : DependencyObject { public event EventHandler ValueChanged; public object Value { get { return (object)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(ValueProxy), new PropertyMetadata(null, OnValueChanged)); private static void OnValueChanged(object sender, DependencyPropertyChangedEventArgs args) { var element = sender as ValueProxy; if (element == null || element.ValueChanged == null) return; element.ValueChanged(element, EventArgs.Empty); } public void Bind(object source, string path) { // Realiza o binding de value com o objeto desejado var binding = new Binding(path); binding.Source = source; binding.Mode = BindingMode.TwoWay; BindingOperations.SetBinding(this, ValueProperty, binding); } } } 

You can use it like this:

<CheckBox>
    <i:Interaction.Behaviors>
        <local:CollectionPropertyBehavior CollectionPropertyPath="MyBooleanProperty" SourcePropertyPath="IsChecked" ItemsSource="{Binding CollectionInViewModel}"/>
    </i:Interaction.Behaviors>
</CheckBox>

It doesn't support collection changes yet (just collection swap), but I believe it can be easily modified to to that. If you want to use it out of the box, you can just add a handler to the CollectionChanged event of your ObservableCollection so it will trigger the ItemsSource update:

observableCollection.CollectionChanged += (s,e) => this.OnPropertyChanged("ItemsSource);

I've posted another example here .

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