我有一个小型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;
}
}
答案 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");
}
}