Sandra
Sandra

Reputation: 37

WPF MVVM: ComboBox SelectedValue binding

Due to feedback in comments, I have updated my question.

I am trying to sort and fill a ComboBox (cbVehicle) based on SelectedValue/Item in another ComboBox (cbLicenseHolder). The binding to the properties is done with a BindableCollection (basically the same as an ObservableCollection).

Below you will see my ViewModel.

namespace Ridel.Hub.ViewModels {

    public class TripViewModel : Conductor<IScreen>.StackNavigation {

        private readonly IWindowManager windowManager;

        private BindableCollection<LicenseHolder> _licenseHolders;
        public BindableCollection<LicenseHolder> LicenseHolders {

            get => _licenseHolders;
            set => SetAndNotify(ref this._licenseHolders, value);
        }

        private BindableCollection<License> _licenses;
        public BindableCollection<License> Licenses {

            get => _licenses;
            set => SetAndNotify(ref this._licenses, value);
        }

        #region Constructor
        public TripViewModel(IWindowManager windowManager) {

            this.windowManager = windowManager;
            LicenseHolders = new BindableCollection<LicenseHolder>();
            Licenses = new BindableCollection<License>();
        }
        #endregion // Constructor

        #region ComboBoxes

        public void FillComboBoxLicenseHolders() {

            try {

                DataSet ds = new DataSet();

                using (SqlConnection sqlCon = new SqlConnection(ConnectionString.connectionString)) {

                    SqlDataAdapter sqlDA = new();
                    sqlDA.SelectCommand = new SqlCommand("select Foretaksnavn from tblLicenseHolder", sqlCon);
                    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];
                    LicenseHolder licenseHolder = new();
                    licenseHolder.Foretaksnavn = dr["Foretaksnavn"].ToString();

                    LicenseHolders.Add(licenseHolder);
                }

            } catch (Exception ex) {

                MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        public void FillComboBoxLicenses() {

            Licenses.Clear();

            try {

                DataSet ds = new();

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

        private string _cbLicenseHolderSelection;
        public string CbLicenseHolderSelection {

            get { return this._cbLicenseHolderSelection; }
            set { SetAndNotify(ref this._cbLicenseHolderSelection, value);

                if (value != null) {

                    FillComboBoxLicenses();
                }
            }
        }

As you can see, I have two BindableCollection, one named LicenseHolders, the other named Licenses. LicenseHolders is bound to cbLicenseHolder, Licenses is bound to cbVehicle.

Also, you see that I have added my method; FillComboBoxLicenses, to the CbLicenseHolderSelection property, and in the XAML underneath, you will see that I have tried to bind the property to the SelectedValue of the cbLicenseHolder.

But I am obviously missing a piece of the picture here. cbVehicle is not being populated.

XAML below:

<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"
/>
        
<ComboBox Name="cbVehicle" Canvas.Left="381" Canvas.Top="204" Width="269"
          ItemsSource="{Binding Licenses, Mode=TwoWay}"
          DisplayMemberPath="KjøretøyID"
          FontSize="12" 
          IsEnabled="True"
          IsSynchronizedWithCurrentItem="True"
/>

Both LicenseHolder.cs and License.cs has implemented PropertyChangedBase. Their properties looks like this:

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

SetAndNotify is a framework function - it does this:

"Takes, by reference, a field, and its new value. If field != value, will set field = value and raise a PropertyChanged notification."

My stored procedure:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[getLicense]
    @Foretaksnavn nvarchar(50)
AS 
SELECT tblLicense.ID, LøyvehaverID, KjøretøyID
FROM tblLicense
INNER JOIN tblLicenseHolder
ON tblLicense.LøyvehaverID = tblLicenseHolder.Foretaksnavn
WHERE tblLicense.LøyvehaverID = @Foretaksnavn

Upvotes: 0

Views: 198

Answers (1)

EldHasp
EldHasp

Reputation: 7918

Take this not as an answer, but as clarifying questions for a better understanding of the problem.
I just need some explanations that include the code, so I have to give them in the form of an answer.

  1. Change the implementation of properties, method and bindings as follows and report the result:
private LicenseHolder _cbLicenseHolderSelection;
public LicenseHolder CbLicenseHolderSelection {

   get { return this._cbLicenseHolderSelection; }
   set { SetAndNotify(ref this._cbLicenseHolderSelection, value);

       if (value != null) {

           FillComboBoxLicenses();
       }
   }
}
<ComboBox Name="cbLicenseHolder" Canvas.Left="50" Canvas.Top="204" Width="291"               
          ItemsSource="{Binding LicenseHolders, Mode=TwoWay}"                 
          Loaded="{s:Action FillComboBoxLicenseHolders}"    
          DisplayMemberPath="Foretaksnavn"
          SelectedItem="{Binding CbLicenseHolderSelection, UpdateSourceTrigger=PropertyChanged}"
          FontSize="12"
/>
public ObservableCollection<License> Licenses {get;}
    = new ObservableCollection<License>();
public void FillComboBoxLicenses() {

    try {

        DataSet ds = new();

        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)
            // It is necessary to specify a specific property for the Value.
                     .Value = CbLicenseHolderSelection.SomeProperty.ToString();
            sqlDA.Fill(ds);
        }

        DataTable dt = new();
        dt = ds.Tables[0];

        Licenses.Clear();

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

    } // On this line (after the for loop), set a breakpoint.

    catch (Exception ex) {

        MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
    }
}

Also tell how many items are in the Licenses collection after stopping in the FillComboBoxLicenses method on the line after the loop.

Upvotes: 1

Related Questions