2

I have the following classes gist with the classes.
I want to bind Item.Visible to Items.ItemsVisible - is it possible?, if so - how?

Item.cs:

using System;
using System.ComponentModel;

namespace WpfApplication85
{
    /// <summary>
    /// Item Object.
    /// </summary>
    public class Item : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged; //Event to notify when Property changed.

        /// <summary>
        /// Notify that Property has Changed.
        /// </summary>
        /// <param name="propertyName">The name of the Property</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region Private Variables

        private bool _Visible; //Bool to determine if the Item is visible or not

        #endregion

        #region Public Properties

        //Return the value of Visible / Set the value of Visible and Notify.
        public bool Visible 
        {
            get { return _Visible; }
            set 
            { 
                _Visible = value;
                NotifyPropertyChanged("Visible");
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Item Constructor
        /// </summary>
        public Item()
        {
            _Visible = true;
        }

        #endregion
    }
}

Items.cs:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WpfApplication85
{
    /// <summary>
    /// Items Object.
    /// </summary>
    public class Items : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged; //Event to notify when Property changed.

        /// <summary>
        /// Notify that Property has Changed.
        /// </summary>
        /// <param name="propertyName">The name of the Property</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region Private Variables

        private bool _itemsVisible; //Bool to determine if the Items are visible or not
        private ObservableCollection<Item> _itemsCollection; //Collection of Items.

        #endregion

        #region Public Properties

        //Return the value of ItemsVisible / Set the value of ItemsVisible and Notify.
        public bool ItemsVisible
        {
            get { return _itemsVisible; }
            set 
            { 
                _itemsVisible = value;
                NotifyPropertyChanged("ItemsVisible");
            }
        }

        //Return the Items Collection / Set the Items Collection and Notify.
        public ObservableCollection<Item> ItemsCollection
        {
            get
            {
                return _itemsCollection;
            }
            set
            {
                _itemsCollection = value;
                NotifyPropertyChanged("ItemsCollection");
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Items Constructor
        /// </summary>
        public Items()
        {
            _itemsVisible = true;
            _itemsCollection = new ObservableCollection<Item>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Add Item to the ItemsCollection.
        /// </summary>
        /// <param name="item">Item Object</param>
        public void AddItem(Item item)
        {
            //Bind item.Visible to this.ItemsVisible
            _itemsCollection.Add(item);
        }

        #endregion
    }
}
Ron
  • 3,975
  • 17
  • 80
  • 130
  • Items is member of Item. So when the value of Items.ItemsVisible property value change. You can loop through each item in the collection and set the same value. – Nps Mar 02 '14 at 13:19
  • I know I can loop... I am trying to find "better" way then looping, thats why I wanted to know if it is possible to bind them – Ron Mar 02 '14 at 13:21
  • How are you going to get a true/false or visible/not from a collection? – paparazzo Mar 02 '14 at 13:36
  • Why do I need to get true/false from a collection? the ItemsVisible property which is a property of the Items class holds the true/false – Ron Mar 02 '14 at 13:38

2 Answers2

2

Setting data binding within Items and Item properties is nothing but listening PropertyChanged or CollectionChanged event from proper interfaces.

You can use either += clause for the subscription, or WeakEventListener pattern, using the PropertyChangedEventManager and CollectionChangedEventManager

I prefer the last one, because:

Listening for events can lead to memory leaks.

So, your Items class should implement IWeakEventListener interface:

public class Items : INotifyPropertyChanged, IWeakEventListener
{
    #region IWeakEventListener

    public bool ReceiveWeakEvent(Type managerType, Object sender, EventArgs e)
    {
        if (sender == this._itemsCollection && managerType == typeof(CollectionChangedEventManager))
        {
            // Your collection has changed, you should add/remove
            // subscription for PropertyChanged event
            UpdateSubscriptions((NotifyCollectionChangedEventArgs)e);
            return true;
        }
        if (sender is Item && managerType == typeof(PropertyChangedEventManager))
        {
            // The Visible property of an Item object has changed
            // You should handle it properly here, for example, like this:
            this.ItemsVisible = this._itemsCollection.All(i => i.Visible);
            return true;
        }

        return false;
    }

    private void UpdateSubscriptions(NotifyCollectionChangedEventArgs e)
    {
        switch(e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (Item item in e.NewItems)
                {
                    PropertyChangedEventManager.AddListener(item, this, "Visible");
                }
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (Item item in e.OldItems)
                {
                    PropertyChangedEventManager.RemoveListener(item, this, "Visible");
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                foreach (Item item in this._itemsCollection)
                {
                    PropertyChangedEventManager.RemoveListener(item, this, "Visible");
                    PropertyChangedEventManager.AddListener(item, this, "Visible");
                }
                break;
             default:
                break;
        }
    }

...
    public Items()
    {
        _itemsVisible = true;
        _itemsCollection = new ObservableCollection<Item>();
        CollectionChangedEventManager.AddListener(_itemsCollection, this);
    }
}
stukselbax
  • 5,855
  • 3
  • 32
  • 54
  • I understand your method and while I am searching for easier solution then looping through my items, you provided something even more complex (using listener) - I think it is more complex? – Ron Mar 02 '14 at 14:28
  • I just show you one more possibility to accomplish the property updates, and I explained why you should try this approach. This is required if you need to change ItemsVisible when some of Item.Visible have changed. If you need to change only Item.Visible properties, you can simply do that in the ItemsVisible setter. – stukselbax Mar 02 '14 at 14:37
1

Binding in the WPF sense only works on DependencyProperties, and does not apply between two standard properties (even when using INotifyPropertyChanged).

That said, if you are using these classes as View Models and are binding them to controls you could use a MultiConverter to set the visibility of the Control to collapsed when both the ItemsVisible and Visible property are true (for example).

Alternatively, you could add a Parent property to the Item class and set it to the parent Items class which would allow you to have the Item.Visible property return the parent's ItemsVisible property (or again whatever logic makes sense in your application).

Andrew Hanlon
  • 7,271
  • 4
  • 33
  • 53