MVVM数据绑定不更新

时间:2017-10-10 09:30:00

标签: c# wpf

我有一个小型WPF应用程序,我开始学习MVVM数据绑定模式。我有一个空的textBox,我把它绑定到" FirstName"但是,当我运行我的代码时,它没有更新。我有一个ObservableObject类,它继承自INotifyPropertyChanged类,以检查属性是否已更新。当我运行代码时,属性会采用正确的值,但UI永远不会更新。

我的代码如下

MainWindow.xaml

<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FirstName}" VerticalAlignment="Center" HorizontalAlignment="Left" TextWrapping="Wrap" FontSize="16" Margin="20,20,0,20" Width="132"></TextBox>

主Windows.xaml.cs

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MainViewModel();
}

Person.cs

class Person:ObservableObject
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }

        set
        {
            _firstName = value;
            RaisePropertyChanged("FirstName");
        }
    }
}

MainViewModel.cs

class MainViewModel:ObservableObject
{
    List<Person> pList = new List<Person>();
    public MainViewModel()
    {
        pList = new List<Person>()
        {
            new Person() {FirstName="Craig"}
        };
        Init();
    }

    public void Init()
    {
        var li = pList.FirstOrDefault();

    }

}

OnservableObject.cs

[Serializable]
public abstract class ObservableObject : INotifyPropertyChanged, IDisposable
{
    #region Constructor

    protected ObservableObject()
    {
    }

    #endregion Constructor

    #region DisplayName

    /// <summary>
    /// Returns the user-friendly name of this object.
    /// Child classes can set this property to a new value,
    /// or override it to determine the value on-demand.
    /// </summary>
    public virtual string DisplayName { get; protected set; }

    #endregion DisplayName

    #region INotifyPropertyChanged Members

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        this.PropertyChanged?.Invoke(this, e);
    }

    protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpresssion)
    {
        var propertyName = PropertySupport.ExtractPropertyName(propertyExpresssion);
        this.RaisePropertyChanged(propertyName);
    }

    protected void RaisePropertyChanged(String propertyName)
    {
        VerifyPropertyName(propertyName);
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged Members

    #region Debugging Aides

    /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyPropertyName(string propertyName)
    {
        // Verify that the property name matches a real,
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        {
            string msg = "Invalid property name: " + propertyName;

            if (this.ThrowOnInvalidPropertyName)
                throw new Exception(msg);
            else
                Debug.Fail(msg);
        }
    }

    /// <summary>
    /// Returns whether an exception is thrown, or if a Debug.Fail() is used
    /// when an invalid property name is passed to the VerifyPropertyName method.
    /// The default value is false, but subclasses used by unit tests might
    /// override this property's getter to return true.
    /// </summary>
    protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

    #endregion Debugging Aides

    #region IDisposable Members

    /// <summary>
    /// Invoked when this object is being removed from the application
    /// and will be subject to garbage collection.
    /// </summary>
    public void Dispose()
    {
        this.OnDispose();
    }

    /// <summary>
    /// Child classes can override this method to perform
    /// clean-up logic, such as removing event handlers.
    /// </summary>
    protected virtual void OnDispose()
    {
    }



    /// <summary>
    /// Useful for ensuring that ViewModel objects are properly garbage collected.
    /// </summary>
    ~ObservableObject()
    {
        string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
        Debug.WriteLine(msg);
    }



    #endregion IDisposable Members
}

PropertySupport.cs

public static class PropertySupport
{
    public static String ExtractPropertyName<T>(Expression<Func<T>> propertyExpresssion)
    {
        if (propertyExpresssion == null)
        {
            throw new ArgumentNullException("propertyExpresssion");
        }

        var memberExpression = propertyExpresssion.Body as MemberExpression;
        if (memberExpression == null)
        {
            throw new ArgumentException("The expression is not a member access expression.", "propertyExpresssion");
        }

        var property = memberExpression.Member as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException("The member access expression does not access a property.", "propertyExpresssion");
        }

        var getMethod = property.GetGetMethod(true);
        if (getMethod.IsStatic)
        {
            throw new ArgumentException("The referenced property is a static property.", "propertyExpresssion");
        }

        return memberExpression.Member.Name;
    }
}

3 个答案:

答案 0 :(得分:1)

您应该在MainViewModel中创建一个属性CurrentPerson或类似的东西。 CurrentPerson应该是例如pList的第一个条目。然后你可以绑定到CurrentPerson.FirstName。

答案 1 :(得分:1)

MainViewModel没有名为FirstName的属性。事实上,它根本就没有财产。

如果您将pList定义为公共属性

class MainViewModel : ObservableObject
{
    public List<Person> pList { get; }

    public MainViewModel()
    {
        pList = new List<Person>()
        {
            new Person() {FirstName="Craig"}
        };
    }
}

...你可以像这样绑定Person中的第一个List<Person>对象:

<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding pList[0].FirstName}" />

或者,您可以添加Person属性,将pList.FirstOrDefault()的值返回到MainViewModel类,并绑定到XAML中的这个属性。

但是你只能绑定到公共属性。您无法绑定到字段。

答案 2 :(得分:0)

您正在尝试将Model的属性直接绑定到UI。 想想你要绑定到TextBox的内容。您有一个Person列表,您可以从中将所选人员的FirstName属性绑定到TextBox。从这个意义上说,在ViewModel中创建一个SelectedPerson属性,并将SelectedPerson.FirstName绑定到可以工作的TextBox。

将以下属性添加到您的viewmodel

situation_id

尝试在XAML中按如下方式绑定

private Person _selectedPerson;
    public Person SelectedPerson
    {
        get { return _selectedPerson; }

        set
        {
            _selectedPerson= value;
            RaisePropertyChanged("SelectedPerson");
        }
    }