Ron
Ron

Reputation: 4095

Bind value from one class to another value in another class

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
    }
}

Upvotes: 2

Views: 1642

Answers (2)

stukselbax
stukselbax

Reputation: 5935

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);
    }
}

Upvotes: 2

Andrew Hanlon
Andrew Hanlon

Reputation: 7421

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).

Upvotes: 1

Related Questions