Welcome to Habanero Land!   Register  |  Login
You are here:   Blogs
  So, why do you need to register to download Habanero and access the additional materials? 
The success of any open source project is based on the community built around it. So go ahead, register / login and get involved!
Blog Roll
 
Categories
 
Search Blog
 
 
Habanero Cloud
 
Habanero Archive
 
Recent Blog Posts
 
 
Apr 1

Written by: brettright
Thursday, April 01, 2010 1:47 PM  RssIcon

ComboBox Linker

Ok so I have to admit that I have had to solve this problem several times over the past 12 years of programming. The problem is fairly generic and up untill now there was no general solution to it in Habanero.Faces.

The problem is when you have two combo boxes that are linked i.e. the data from the first combo box filters the data in the second combo box. e.g. Select a Country and you get a filtered list of States for that country.

I have just quickly created a class that links these combo boxes and does all the filtering for you. The class is incredibly simple but I have found it incredibly usefull on my current project.

This class is included with tests in the Habanero.Faces community project which is due to be released next week.

      ///<summary>
    /// This class solves the fairly generic problem of filtering one ComboBox based on the selected item in another combobox.
    /// The problem is when you have two combo boxes that are linked
    ///     i.e. the data from the first combo box filters the data in the second combo box.
    ///      e.g. Select a Country and you get a filtered list of States for that country.
    ///</summary>
    ///<typeparam name="TParentType">The Type of the Parent Business Object(In our example the Country) </typeparam>
    ///<typeparam name="TChildType">The Type of the Child Business Object (in our example the State)</typeparam>
    public class ComboBoxLinker<TParentType, TChildType>
            where TChildType : class, IBusinessObject, new()
            where TParentType: IBusinessObject
    {
        ///<summary>
        /// The Parent Combo Box Selector in our example the Countries.
        ///</summary>
        public IBOComboBoxSelector ParentSelector { get; private set; }
        /// <summary>
        /// The Child Combo Box Selector in our example the States.
        /// </summary>
        public IBOComboBoxSelector ChildSelector { get; private set; }
        /// <summary>
        /// The name of the relationship that is linking these two Business Objects and hence these two ComboBoxes.
        /// in our example the "States" relationship on the Business Object Country.
        /// </summary>
        public string RelationshipName { get; private set; }
        /// <summary>
        /// The Constructor
        /// </summary>
        /// <param name="parentSelector"></param>
        /// <param name="childSelector"></param>
        /// <param name="relationshipName"></param>
        public ComboBoxLinker(IBOComboBoxSelector parentSelector, IBOComboBoxSelector childSelector, string relationshipName)
        {
            if (parentSelector == null) throw new ArgumentNullException("parentSelector");
            if (childSelector == null) throw new ArgumentNullException("childSelector");
            if (string.IsNullOrEmpty(relationshipName)) throw new ArgumentNullException("relationshipName");
            ParentSelector = parentSelector;
            ChildSelector = childSelector;
            RelationshipName = relationshipName;
            parentSelector.SelectedIndexChanged += this.ParentComboBox_SelectedIndexChanged;
        }

        private void ParentComboBox_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            ChildSelector.BusinessObjectCollection = null;

            var selectedItem = ParentSelector.SelectedItem;
            if (selectedItem == null || Convert.ToString(selectedItem) == "") return;
            TParentType location = (TParentType) selectedItem;
            MultipleRelationship<TChildType> relationship =
                location.Relationships[RelationshipName] as MultipleRelationship<TChildType>;
            if (relationship == null) return;
            ChildSelector.BusinessObjectCollection = relationship.BusinessObjectCollection;
        }
    }

7 comment(s) so far...


Gravatar

Re: Filtering a Combo Box based on another Combo Box.

I'd love to see the Trigger functionality back in place... it was so promising.

By EricSavage on   Monday, May 03, 2010 5:29 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

How would one use this class? For example if I had a form defined in my ClassDefs that had the two combo boxes, would I still need to, create a custom BOEditor and give this class the two combo-boxes from the PanelInfo's?

By Hagashen on   Thursday, May 06, 2010 12:39 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

Eric
Yes I agree that the Trigger functionality should be driven back in. This is kinda a first step towards this.
The objective really being that we build this from objects and once the objects work really well then look at generating.

By brettright on   Thursday, May 06, 2010 5:12 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

Hagashen
Basically I was using this as a filter for a grid and also for a filter for a report in fact managed to reuse the controll doign this cascading filtering in 9 places in the system.

I am guessing that if you wanted to use it to update data i.e. in a BOPanel then you would only have the data in the second control updating your business object (in my case the asset should only be linked to the Ward and the Ward is filtered by the Department.
I would guess a custom control doing this filtering would be bound to the Ward Relationship using a mapper something like AutoLoadingRelationshipComboBoxMapper.

By brettright on   Thursday, May 06, 2010 6:31 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

Currently for editing a BO using a BOEditorForm you have to do some "interesting" things. Let's say I have a Person linked to an Institution which is linked to a District, and on my form I want to allow the user to select an Institution, but because the list is so big I want to allow them to select a District first to filter the Institutions. To do this, one way I've used is:

1. Add a DistrictID field to Person, and make it non-persistable. To make it non-persistable you have to write code because there's no way to make a property non-persistable in the xml. This isn't ideal as your app's runtime behaviour is then different to what is declared in the xml.
2. Add a combobox for DistrictID on to your UI
3. Inherit from BOEditorForm, and link up the District and Institution combo boxes (using the class Brett posted)

It's not particularly ideal, and common enough that a new control could be implemented to do it, so for example, a FilteringComboBoxControl, where you give it the parent type (District in my example) and the actual property.

The trigger concept wouldn't correct the problem of having to add the unnecessary non-persistable property to the class, so some other solution (like this FilteringComboBoxControl) would be needed.

By Peter Wiles on   Thursday, May 13, 2010 1:19 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

So is this control being developed or just a plan at this stage?

By Hagashen on   Thursday, May 13, 2010 1:41 PM
Gravatar

Re: Filtering a Combo Box based on another Combo Box.

Yes Pete i agree
The Custom Control I was talking about in my response to Hagashed would be even better if we generalised it into a Filtering ComboBoxControl and a AutoLoadingRelationshipFilteringComboBoxMapper.
You would then have to set the Parent Relationship (District in your example) and Potentially the Parents Parent (maybe Province). Getting the XML for this might be tough but with Code it would be incredibly simple to do.
This would involve building your UIView programmatically instead of in xml or modifying the UIView once it is loaded.
Brett

By brettright on   Thursday, May 13, 2010 4:08 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Add Comment   Cancel 

This website is best viewed in Internet Explorer 7 & 8; Firefox; Opera; & Safari.

Get Habanero at SourceForge.net. Fast, secure and Free Open Source software downloads