C# 模型属性未命中WPF MVVM项目中的集访问器

C# 模型属性未命中WPF MVVM项目中的集访问器,c#,wpf,mvvm,C#,Wpf,Mvvm,我正在学习和创建我的第一个WPF应用程序,试图实现MVVM设计模式,但是我似乎无法理解为什么这个属性没有启动它的Set访问器,所以我可以使用我拥有的OnPropertyChanged方法。如果您能解释一下为什么这不能像我预期的那样起作用,我将不胜感激 我不明白的是,在ViewModel中的GetChargeUnits方法中,我正在创建我的ChargeUnits模型的一个实例,并将属性设置为读取器的结果。该读取器是否返回属性设置为罚款的结果?但是,当单步执行时,它从未到达属性中的设置行,因此我无法

我正在学习和创建我的第一个WPF应用程序,试图实现MVVM设计模式,但是我似乎无法理解为什么这个属性没有启动它的Set访问器,所以我可以使用我拥有的OnPropertyChanged方法。如果您能解释一下为什么这不能像我预期的那样起作用,我将不胜感激

我不明白的是,在ViewModel中的GetChargeUnits方法中,我正在创建我的ChargeUnits模型的一个实例,并将属性设置为读取器的结果。该读取器是否返回属性设置为罚款的结果?但是,当单步执行时,它从未到达属性中的设置行,因此我无法检测它是否已更改。这个方法中的注释部分是我最初使用的,我尝试了许多组合

请帮忙,谢谢

型号:

public class ChargeUnit : INotifyPropertyChanged
{
    private string _chargeUnitDescription;
    private int _chargeUnitListValueId;
    public event PropertyChangedEventHandler PropertyChanged;

    public ChargeUnit()
    {

    }
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public string ChargeUnitDescription
    {
        get { return _chargeUnitDescription; }
        set
        {
            _chargeUnitDescription = value;
            OnPropertyChanged("ChargeUnitDescription");
        }
    }

    public int ChargeUnitListValueId
    {
        get { return _chargeUnitListValueId; }
        set
        {
            _chargeUnitListValueId = value;
            OnPropertyChanged("ChargeUnitListValueId");
        }
    }
视图模型:

public class ClientRatesViewModel
{
    private IList<ClientRates> _clientRatesPreAwr;
    private IList<ClientRates> _clientRatesPostAwr;
    private List<ChargeUnit> _chargeUnits;
    private const string _connectionString = @"connectionString....";
    public ClientRatesViewModel()
    {
        _clientRatesPreAwr = new List<ClientRates>
        {
            new ClientRates {ClientRatesPreAwr = "Basic"}
        };

        _clientRatesPostAwr = new List<ClientRates>
        {
            new ClientRates{ClientRatesPostAwr = "Basic Post AWR"}
        };

        _chargeUnits = new List<ChargeUnit>();
    }

    public IList<ClientRates> ClientRatesPreAwr
    {
        get { return _clientRatesPreAwr; }
        set { _clientRatesPreAwr = value; }
    }

    public IList<ClientRates> ClientRatesPostAwr
    {
        get { return _clientRatesPostAwr; }
        set { _clientRatesPostAwr = value; }
    }

    public List<ChargeUnit> ChargeUnits
    {
        get { return _chargeUnits; }
        set { _chargeUnits = value; }
    }

    public List<ChargeUnit> GetChargeUnits()
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            using (var command = new SqlCommand("SELECT LV.ListValueId, LV.ValueName FROM tablename", connection))
            {
                command.CommandType = CommandType.Text;

                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var test = new ChargeUnit();
                        test.ChargeUnitDescription = reader["ValueName"].ToString();
                        //_chargeUnits.Add(new ChargeUnit
                        //{
                        //    ChargeUnitDescription = reader["ValueName"].ToString(),
                        //    ChargeUnitListValueId = (int)reader["ListValueId"]
                        //});
                    }
                }


            }

        }
        return new List<ChargeUnit>();
    }

我认为这是一个误会

这样想:为什么模型中的描述会发生变化?您没有更改该属性的内容

您描述的是,您正在更改组合框的SelectedItem。模型实例get的get被命中,因为ComboBox需要访问它来显示您在绑定中使用ComboBox的DisplayMemberPath属性定义的字符串

您感兴趣的是SelectedItem或SelectedValue,具体取决于组合框的设置

下面是一个简单的例子来说明这一点:

    namespace SOBindingTest1
{
    public partial class MainWindow : Window
    {
        public ClientRatesViewModel crvm { get; set; }
        public MainWindow()
        {
            InitializeComponent();
            crvm = new ClientRatesViewModel();
        }


        public class ChargeUnit : INotifyPropertyChanged
        {
            private string _chargeUnitDescription;
            private int _chargeUnitListValueId;

            public ChargeUnit()
            {

            }

            public string ChargeUnitDescription
            {
                get { return _chargeUnitDescription; }
                set
                {
                    _chargeUnitDescription = value;
                    OnPropertyChanged();
                }
            }

            public int ChargeUnitListValueId
            {
                get { return _chargeUnitListValueId; }
                set
                {
                    _chargeUnitListValueId = value;
                    OnPropertyChanged();
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
            {
                PropertyChangedEventHandler tmp = this.PropertyChanged;
                if (tmp != null)
                {
                    tmp(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        public class ClientRatesViewModel : INotifyPropertyChanged
        {
            public ClientRatesViewModel()
            {
                _chargeUnits = new List<ChargeUnit>();
                for (int i = 0; i < 10; i++)
                {
                    _chargeUnits.Add(new ChargeUnit() { ChargeUnitDescription = i.ToString(), ChargeUnitListValueId = i });
                }
            }

            protected ChargeUnit _SelectedItem;
            public ChargeUnit SelectedItem
            {
                get
                {
                    return this._SelectedItem;
                }
                set
                {
                    if(this._SelectedItem == value)
                    {
                        return;
                    }
                    this._SelectedItem = value;
                    this.OnPropertyChanged();
                }
            }


            private List<ChargeUnit> _chargeUnits;
            public List<ChargeUnit> ChargeUnits
            {
                get { return _chargeUnits; }
                set 
                {
                    if(this._chargeUnits == value)
                    {
                        return;
                    }
                    _chargeUnits = value;
                    this.OnPropertyChanged();
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
            {
                PropertyChangedEventHandler tmp = this.PropertyChanged;
                if (tmp != null)
                {
                    tmp(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
}   
以及相应的xaml:

<Window x:Class="SOBindingTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SOBindingTest1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto"/>
         <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="*" />
         <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <TextBlock Text="Bla:"  HorizontalAlignment="Right" Margin="10"/>
      <ComboBox Name="cboTest" Grid.Column="1" Margin="3" DisplayMemberPath="ChargeUnitDescription" SelectedValuePath="ChargeUnitListValueId" ItemsSource="{Binding Path=crvm.ChargeUnits, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}" SelectedItem="{Binding Path=crvm.SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"/>
   </Grid>
</Window>
更新:

更改了代码,使其将选定项填充到ViewModel的属性中。如果您在setter中放置断点,那么如果您在组合框中选择某个内容,它就会被命中

对于您将来可能提出的问题,请提供一个答案,并据此提出您的问题。那会节省很多时间

更新2:
将INotifyPropertyChanged添加到ViewModel示例中

是否检查了获取程序和设置程序的VS设置?这是针对VS2013的,但你可能可以在谷歌上搜索其他版本。转到工具->选项->调试->常规。然后在右侧取消选中Step over properties和operators Managedonly@FrankJ你搞定了!谢谢lot@FrankJ刚意识到这是问题的一部分,因为我在ViewModel中设置它时,它正在跳入集合,但是当我更改组合框中的值时,我的模型中的ChargeUnitDescription属性从未到达集合,但是,它总是返回我刚才选择的更新值?是否在Setter代码中设置了断点?仅在单步执行代码时,绑定传播的更改不容易观察到。你需要一个断点。如果这不起作用,请发布一个我可以在VS中运行的最小代码示例,并根据该示例描述您正在做的事情、正在发生的事情以及您预期会发生的事情。@FrankJ如果您参考我上面发布的模型,ChargeUnitDescription的setter中会有一个断点。当我在组合框中选择任何项目时,它都不会被击中。但是,每次都会命中get,并且_chargeUnitDescription包含组合框中显示的chargeUnitDescription。我的Xaml comboboxs displaymemberpath也是ChargeUnitDescriptionTanks的属性,感谢您的帮助Frank,但我认为处理事件不会遵循MVVM设计模式。我看过很多关于绑定到属性的帖子,当这个值改变属性时,他就改变了集合。这似乎不起作用