WPF MVVM:组合框 SelectedValue 绑定

Posted

技术标签:

【中文标题】WPF MVVM:组合框 SelectedValue 绑定【英文标题】:WPF MVVM: ComboBox SelectedValue binding 【发布时间】:2021-10-11 22:09:15 【问题描述】:

由于 cmets 的反馈,我更新了我的问题。

我正在尝试根据SelectedValue/Item 在另一个ComboBox (cbLicenseHolder) 中对ComboBox (cbVehicle) 进行排序和填充。 与属性的绑定是通过BindableCollection 完成的(与ObservableCollection 基本相同)。

下面你会看到我的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();
                
            
        

如您所见,我有两个BindableCollection,一个名为LicenseHolders,另一个名为LicensesLicenseHolders 绑定到cbLicenseHolderLicenses 绑定到cbVehicle

另外,你看到我已经添加了我的方法; FillComboBoxLicenses,到CbLicenseHolderSelection 属性,在下面的XAML 中,你会看到我已经尝试将属性绑定到cbLicenseHolderSelectedValue

但我显然在这里遗漏了一部分图片。 cbVehicle 没有被填充。

XAML 如下:

<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"
/>

LicenseHolder.csLicense.cs 都实现了PropertyChangedBase。 它们的属性如下所示:

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

SetAndNotify 是一个框架函数 - 它这样做:

"通过引用获取一个字段及其新值。如果字段 != 值, 将设置 field = value 并引发 PropertyChanged 通知。”

我的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

【问题讨论】:

旁注:SQL 操作不属于 ViewModel。 您可以删除所有 SQL 代码(保持问题简短)。我们只需要知道列表不是空的。 Licenses 应该是一个 BindingList 和/或您需要在填写后调用 PropertChanged(nameof(Licenses))。 另外请不要把两个问题合二为一。改为问两个问题。 我建议为每个问题创建一个问题。 【参考方案1】:

不要将此作为答案,而是将其作为澄清问题以更好地理解问题。 我只需要一些包含代码的解释,所以我必须以答案的形式给出。

    如下更改属性、方法和绑定的实现并报告结果:
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);
    

在循环后的行上的 FillComboBoxLicenses 方法中停止后,还告诉 Licenses 集合中有多少项。

【讨论】:

感谢您抽出宝贵时间撰写此内容丰富的回复!我已经完成了您建议的更改,当我使用断点运行它时,许可证集合中有两个项目,它们与我的特定实体的数据库相关,但它们没有显示为它们的格式在我的数据库中,但被列为 Ridel.Hub.License。 Ridel.Hub.License 是该类型的字符串表示形式。它们在哪里这样显示?在名为“cbVehicle”的组合框中?如果是这样,那么您错误地设置了 DisplayMemberPath。 只有当我调试和检查集合时,它才会加载正确数量的项目,但正如你所说,只是 type 。 ComboBox 在运行时仍然是空的。我不明白也无法理解它 那么根据你的代码,是找不到原因的。这些可能是:ViewModel 实例中的混淆、属性名称错误、绑定错误等等。如果你可以使用“动态可视化树”和“动态属性浏览器”,那么你需要查看它们的数据上下文、元素状态、绑定错误等。如果你不知道如何使用它们,那么创建一个简短的解决方案(请务必排除数据库并将其替换为返回一些数据的测试代码)。把它写在 GitHub 上并给出一个链接。我会看看可能是什么问题。

以上是关于WPF MVVM:组合框 SelectedValue 绑定的主要内容,如果未能解决你的问题,请参考以下文章

在 MVVM 中的 Datagrid 中绑定 WPF 组合框不保存更改

WPF MVVM:组合框 SelectedValue 绑定

我如何在wpf mvvm中使用组合框

ItemsControl 组合框 selecteditem C# WPF MVVM

使用 MVVM 重置组合框选定项目

MVVM WPF ComboBox SelectedValue 不是确切值