question

OleMartinGulbrandsen-4027 avatar image
0 Votes"
OleMartinGulbrandsen-4027 asked RezaJaferi1992 commented

MVVM: filtering ComboBox2 based on SelectedValue in ComboBox1

Hello!

I am writing a business application in WPF, adhering to the MVVM design principles.

What I am trying to do:

I have two ComboBoxes in a View. I want the 2nd one to be filtered based on the SelectedItem/Value in the first one.
I can't figure out how to do that from the ViewModel. This is what I've got so far:

My first ComboBox:

 <ComboBox Name="cbLicenseHolder" Canvas.Left="50" Canvas.Top="204" Width="291"               
        ItemsSource="{Binding LicenseHolders, Mode=TwoWay}"                 
        Loaded="{s:Action FillComboBoxLicenseHolders}"    
        DisplayMemberPath="Foretaksnavn"
        SelectedValue="{Binding CbLicenseHolderSelection, UpdateSourceTrigger=PropertyChanged}"
        FontSize="12" SelectionChanged="cbLicenseHolder_SelectionChanged"                  
        />

My second ComboBox:

 <ComboBox Name="cbVehicle" Canvas.Left="381" Canvas.Top="204" Width="269"
         ItemsSource="{Binding Licenses, Mode=TwoWay}"
         Loaded="{s:Action FillComboBoxLicenses}"
         FontSize="12" 
         IsEnabled="False"/>

"CbLicenseHolderSelection" is a property holder in my ViewModel:


 private string _cbLicenseHolderSelection;
 public string CbLicenseHolderSelection {
    
      get { return this._cbLicenseHolderSelection; }
      set { SetAndNotify(ref this._cbLicenseHolderSelection, value); }
 }

Now, for the loaded event, you can see I am using Stylets (framework) "s:Action" function, to fire off my "FillComboBoxLicenses" -method, which looks like this:

 public void FillComboBoxLicenses() {

     try {
    
         DataSet ds = new DataSet();
    
         using (SqlConnection sqlCon = new SqlConnection(ConnectionString.connectionString)) {
    
             SqlDataAdapter sqlDA = new();
             sqlDA.SelectCommand = new SqlCommand("fillLicensesComboBox", sqlCon);
             sqlDA.SelectCommand.CommandType = CommandType.StoredProcedure;
             sqlDA.SelectCommand.Parameters.Add("@Foretaksnavn", SqlDbType.NVarChar).Value = CbLicenseHolderSelection.ToString();
             sqlDA.Fill(ds);
         }
    
         DataTable dt = new();
         dt = ds.Tables[0];
    
         for (int i = 0; i < dt.Rows.Count; i++) {
    
             DataRow dr = dt.NewRow();
             dr = dt.Rows[i];
             License license = new();
             license.KjøretøyID = dr["KjøretøyID"].ToString();
    
             Licenses.Add(license);
         }
    
     } catch (Exception ex) {
    
         MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
     }
 }

But I get the error message saying: 'Object reference not set to an instance of an object.' I guess because I haven't been able to make a selection in ComboBox1 before it fires off.

How can I make sure my method fires of only when there's been made a selection in the first ComboBox?

Thanks!!



windows-wpf
· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Can you explain more? I had almost the same problem when using the ComboBox SelectionChanged event. Try using the ComboBox DropDownClosed event.

0 Votes 0 ·

In most cases, ComboBox SelectionChanged returns the null value.

0 Votes 0 ·

If you can explain with an example that what you want to filter, I will try to write the code for you.

0 Votes 0 ·

1 Answer

Viorel-1 avatar image
0 Votes"
Viorel-1 answered

Maybe Licenses is null? Try determining the problematic line and maybe remove 'dt.NewRow()'.


· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi Viorel-1! Thanks for commenting.
The application stops at this line:
sqlDA.SelectCommand.Parameters.Add("@Foretaksnavn", SqlDbType.NVarChar).Value = CbLicenseHolderSelection.ToString();

I believe, because it's set to the loaded event. And the code is looking for a selected item in ComboBox1, which I havent been able to select yet, since the code - and therefore the error message- fires before I get the chance. But I don't know how I can make it so that it only triggers AFTER a selection has been made.

0 Votes 0 ·
Viorel-1 avatar image Viorel-1 OleMartinGulbrandsen-4027 ·

The implementation does not seem complicated without design patterns. Maybe remove the Loaded attribute from the second combobox, and call FillComboBoxLicenses from cbLicenseHolder_SelectionChanged, or from setter of CbLicenseHolderSelection, or somehow add an event that watches for CbLicenseHolderSelection.


0 Votes 0 ·

Thank you for the suggestions!
I tried both of those, but it did not work unfortunately. I am sure I am doing something wrong.

     public string CbLicenseHolderSelection {

         get { return this._cbLicenseHolderSelection; }
         set { SetAndNotify(ref this._cbLicenseHolderSelection, value);
             if (value != null)
                 FillComboBoxLicenses();
         }
     }

Is that a proper way of doing it?

0 Votes 0 ·
Show more comments