Resolved Combobox Filter

Daz66

Active member
Joined
May 17, 2020
Messages
40
Programming Experience
Beginner
A novice question if I may.

I'm trying to cascade filter comboboxes to DGV in my winforms project.

I have a list derived from a class that populates cbx1 with CategoryA items, I'm trying to populate cbx2 with CategoryB items in the SelectedIndexChanged event based on the choice in cbx1 and displaying only the relevant categories in cbx2. It works as expected but without the filter.
The commented out LINQ is an attempt to do this but returns errors.

A point in the right direction would be great, thanks.

C#:
void cbxCategoryAFill()
        {
            List<Screwfix> scr = sc.GetAllScrewfixResults();

            this.comboBox1.DataSource = scr.Select(t => t.CategoryA).Distinct().ToList();
            comboBox1.Text = "--Select--";
        }
        void cbxCategoryBFill()
        {
            List<Screwfix> scr = sc.GetAllScrewfixResults();
            
            this.comboBox2.DataSource = scr.Select(t => t.CategoryB).Distinct().ToList();
            comboBox2.Text = "--Select--";
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate { comboBox1.Text = "-Select-"; });//Note Resets combobox after selection

            List<Screwfix> scr = sc.GetAllScrewfixResults();
            dgvLeft.DataSource = scr.Where(x => x.CategoryA.Contains(comboBox1.Text)).ToList();

            // comboBox2.DataSource = ((IEnumerable<string>)dgvLeft.DataSource)
            //.Where(x =>
            //{
            //    return x == (string)comboBox1.SelectedValue;
            //});
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate { comboBox1.Text = "-Select-"; });

            List<Screwfix> scr = sc.GetAllScrewfixResults();

            dgvLeft.DataSource = scr.Where(x => x.CategoryB.Contains(comboBox2.Text)).ToList();
        }
 
I don't know why the ValueMember didn't work correctly and return just the Cat2Id which is an in. Based on the error you described in post #9, it looks like it is returning the entire ScrewfixCat2 object. As a workaround, you could use that to your advantage:
C#:
var cat2Id = ((ScrewfixCat2)cbxCat2.ComboBox.SelectedValue).Cat2Id;
The form will load now but there is system info in the cbxCat2 combo.
Yes it's the filtering on the third combo which is the issue. With the attached code all three combo's will load the correct elements, and cbx2 will filter from cbx1 and then error on the cbx3 which makes me think the display/value members are working ok?
C#:
private void cbxCat1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate { cbxCat1.Text = "-Select-"; });//Note Resets combobox after selection

            IList<Screwfix> scr = sc.GetAllScrewfixResults();
            IList<ScrewfixCat2> scr2 = sc2.allCat2();


            var catId = (int)cbxCat1.ComboBox.SelectedValue;
            cbxCat2.ComboBox.DataSource = scr2.Where(c => c.CatId == catId).ToList();

            dgvLeft.DataSource = scr.Where(s => s.CategoryA.Contains(cbxCat1.Text)).ToList();
            cbxCat2.Enabled = true;
        }

        private void cbxCat2_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate { cbxCat2.Text = "-Select-"; });

            IList<Screwfix> scr = sc.GetAllScrewfixResults();
            //IList<ScrewfixCat3> scr3 = sc3.allCat3();
            //var cat2Id = (int)cbxCat2.ComboBox.SelectedValue;//Old
            //var cat2Id = ((ScrewfixCat2)cbxCat2.ComboBox.SelectedValue).Cat2Id;//New
            //cbxCat3.ComboBox.DataSource = scr3.Where(c => c.Cat2Id == cat2Id).ToList();

            dgvLeft.DataSource = scr.Where(s => s.CategoryB.Contains(cbxCat2.Text)).ToList();
            cbxCat3.Enabled = true;
        }

        private void cbxCat3_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate { cbxCat3.Text = "-Select-"; });
            IList<Screwfix> scr = sc.GetAllScrewfixResults();
            dgvLeft.DataSource = scr.Where(s => s.CategoryC.Contains(cbxCat3.Text)).ToList();
            
        }
 
then error on the cbx3
What error are you getting?

Also why are you changing the text of the combobox on line 32?
 
What error are you getting?

Also why are you changing the text of the combobox on line 32?
Same as before, I get the error on form load with the original code;
System.InvalidCastException: 'Unable to cast object of type 'System.Int32' to type 'Business.ClassLibrary.ScrewfixCat2'.'

With your code snippet the form loads but the elements don't load in cbx2, i get system info instead, if i click the system info i then get the above error.
Really weird as the 2 combo set up works flawlessly.

I like to default the combo's to 'Select' on load and after selection.
 
I like to default the combo's to 'Select' on load and after selection.
Why? Are your users so simple that they don't understand how a basic GUI works? Do you also default a TextBox to "Type"? If you think the users can work out how to do one, why do you think they can't work out how to do the other?
 
With your code snippet the form loads but the elements don't load in cbx2, i get system info instead,
What do you mean "system info"? Was it something like "Business.ClassLibrary.ScrewfixCat2"? If so, it sounds like your DisplayMember seeing doesn't work either. I suspect that your cbxCategoryBFill() was not called. Set a breakpoint on that method to make sure that it is being called.
 
Why? Are your users so simple that they don't understand how a basic GUI works? Do you also default a TextBox to "Type"? If you think the users can work out how to do one, why do you think they can't work out how to do the other?
God I love straight talk! :).
It's a personal app for work so there's just me using it so far, but who knows, If I keep practicing hard and getting good advice like this. (y)
 
What do you mean "system info"? Was it something like "Business.ClassLibrary.ScrewfixCat2"? If so, it sounds like your DisplayMember seeing doesn't work either. I suspect that your cbxCategoryBFill() was not called. Set a breakpoint on that method to make sure that it is being called.
Hello again, thanks for your patience.
yes, that's the exactly what's filling the combo. I set a breakpoint as you suggested and the exception was thrown straight away.
One thing I'm noticing is, when using the original filter code, I get the parent table Id(CatId) numbers in cbxCat2
 
I set a breakpoint as you suggested and the exception was thrown straight away.
Your breakpoint should have been hit first. It shouldn't have thrown an exception first. This suggests that your cbxCategoryBFill() is never called. That would explain why the DisplayMember and ValueMember don't seem to be working on that second combobox.
 
Update
Your breakpoint should have been hit first. It shouldn't have thrown an exception first. This suggests that your cbxCategoryBFill() is never called. That would explain why the DisplayMember and ValueMember don't seem to be working on that second combobox.
Based on what you said, I've just moved the methods from 'form_load' to under 'InitializeComponent' and now I don't get the exception but the filter is not working on the third combo.
 
Set a breakpoint on cbxCat3_SelectedIndexChanged(). Inspect the value of cbxCat3.Text. Is it a reasonable value? If so, then that's the actual results of your filter.
 
Last edited:
Set a breakpoint on cbxCat3_SelectedIndexChanged(). Inspect the value of cbxCat3.Text. Is it a reasonable value? If so, then that's the actual results of your filter.
We're in business.
Was my main problem here the fact that form_Load is a private void?
 
No.

It's the sequence of events. The constructor creates the form and stages various settings for the controls that will show up on the form. The load event is fired before the form is shown, but after controls have actually been created from the point of view of Windows. At that point in time, events start getting fired off, including when items are added to controls or selected items change within the controls as items are added. Since your selected item change events were dependent on the controls having the correct display members and value members set, then things went awry.
 
No.

It's the sequence of events. The constructor creates the form and stages various settings for the controls that will show up on the form. The load event is fired before the form is shown, but after controls have actually been created from the point of view of Windows. At that point in time, events start getting fired off, including when items are added to controls or selected items change within the controls as items are added. Since your selected item change events were dependent on the controls having the correct display members and value members set, then things went awry.
I have a lot to learn, but It's great fun.
Thanks for sticking with me, your efforts have been greatly appreciated.
 
God I love straight talk! :).
It's a personal app for work so there's just me using it so far, but who knows, If I keep practicing hard and getting good advice like this. (y)
I see this particular thing quite often and it always annoys me. It seems to be a "because I can" sort of thing that doesn't actually achieve anything useful. No one who knows anything about using GUI software needs it pointed out that they should select an item from a drop-down and anyone who does need to be told that needs far more help than just that. You can obviously do what you want but the number of people who do this pointless thing and end up making life hard for themselves as a result is amazing.
 
And furthermore, on any decent desktop GUI framework, as well as any modern web UI backed by JavaScript, and modern phone GUIs, you can detect whether the user has selected an item within a list. It was only the older web-based UI before the dominance of JavaScript that needed combo boxes where there was a placeholder value for "no selection made". This was because in the older web-based UI, there was no easy way to communicate from the HTML control back to the web server that the user had not touched the combo box and not selected anything. With modern GUIs, you can enforce that the user has made a selection since you can watch every move they make and keep track of what they have done.
 
Back
Top Bottom