我正在尝试使用this example设置有效的双向更新。
以下是相关的代码段:
XAML:
<Button Click="clkInit">Initialize</Button>
<Button Click="clkStudent">Add student</Button>
<Button Click="clkChangeStudent">Change students</Button>
(...)
<TabControl Name="tabControl1" ItemsSource="{Binding StudentViewModels}" >
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StudentFirstName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Label Content="First Name" Name="label1" />
<TextBox Name="textBoxFirstName" Text="{Binding Path=StudentFirstName}" />
<Label Content="Last Name" Name="label2" />
<TextBox Name="textBoxLastName" Text ="{Binding Path=StudentLastName}" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
主窗口:
public partial class MainWindow : Window
{
internal MainWindowViewModel myMWVM;
public MainWindow()
{
InitializeComponent();
}
private void clkInit(object sender, RoutedEventArgs e)
{
myMWVM= new MainWindowViewModel();
DataContext = myMWVM;
}
private void clkStudent(object sender, RoutedEventArgs e)
{
myMWVM.StudentViewModels.Add(new StudentViewModel());
}
// For testing - call a function out of the student class to make changes there
private void clkChangeStudent(object sender, RoutedEventArgs e)
{
for (Int32 i = 0; i < test.StudentViewModels.Count; i++)
{
myMWVM.StudentViewModels.ElementAt((int)i).changeStudent();
}
}
}
主要观点:
class MainWindowViewModel : INotifyPropertyChanged
{
ObservableCollection<StudentViewModel> _studentViewModels =
new ObservableCollection<StudentViewModel>();
// Collection for WPF.
public ObservableCollection<StudentViewModel> StudentViewModels
{
get { return _studentViewModels; }
}
// Constructor. Add two stude
public MainWindowViewModel()
{
_studentViewModels.Add(new StudentViewModel());
_studentViewModels.Add(new StudentViewModel());
}
// Property change.
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
学生观点:
class StudentViewModel : INotifyPropertyChanged
{
Lazy<Student> _model;
string _studentFirstName;
public string StudentFirstName
{
get { return _studentFirstName; }
set
{
if (_studentFirstName != value)
{
_studentFirstName = value;
_model.Value.StudentFirstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}
string _studentLastName;
public string StudentLastName
{
get { return _studentLastName; }
set
{
if (_studentLastName != value)
{
_studentLastName = value;
_model.Value.StudentLastName = value;
OnPropertyChanged("StudentLastName");
}
}
}
public void changeStudent()
{
_model.Value.changeStudent();
}
public StudentViewModel()
{
_studentFirstName = "Default";
_model = new Lazy<Student>(() => new Student());
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
学生:
class Student
{
public string StudentFirstName { get; set; }
public string StudentLastName { get; set; }
public Student()
{
MessageBox.Show("Student constructor called");
}
public Student(string nm)
{
StudentLastName = nm;
}
public void changeStudent()
{
StudentLastName = "McDonald";
}
}
如果你读到这里我已经感谢了你:)仍然,通过调用“clkChangeStudent”我没有看到文本框中的变化。我想这是因为我没有调用StudentViewModel的set方法。我正在研究的项目有点复杂,很多事情都发生在班级(这里是学生)本身。
如何通过Student-class本身的设置值更新文本框?
答案 0 :(得分:3)
您的实际代码显然不会通知对界面的更改。原因很简单。更改学生姓名的方法位于学生模型中,该模型未实现INotifyPropertyChanged。
根据一个问题,有两个解决方案可以解决这个问题,changeStudent()方法是否必须坚持使用对象模型,也就是说,您的需求是否允许您将changeStudent()方法移动到视图中模型?
如果是,那么,第一个解决方案,只需从模型中删除changeStudent方法并将其移动到视图模型,如下所示:
class StudentViewModel : INotifyPropertyChanged
{
...
public void changeStudent()
{
this.StudentLastName = "McDonald";
}
}
在另一种情况下,第二个解决方案,您必须在模型属性更改时引发事件,然后让您的视图模型继承这些更改。你可以在模型中继续这样做:
class Student : INotifyPropertyChanged
{
...
private string studentLastName;
public string StudentLastName
{
get
{
return this.studentLastName;
}
set
{
if(this.studentLastname != value)
{
this.studentLastName = value;
this.OnPropertyChanged("StudentLastName");
}
}
}
}
对于视图模型:
class StudentViewModel : INotifyPropertyChanged
{
...
public StudentViewModel(Student model)
{
this._model = model;
this._model.PropertyChanged += (sender, e) =>
{
if(e.PropertyName == "StudentLastName")
{
this.OnPropertyChanged("StudentLastName");
}
};
}
}
两种解决方案都有效。非常重要的是,您了解代码明确需要在值发生变化时通知接口。
答案 1 :(得分:1)
ChangeStudent不会调用在视图模型中触发属性notify事件的任何方法,而是改变底层模型。正是这些事件触发了视图自我更新。
另外,您还应该从视图中查看命令绑定,而不是在代码隐藏中使用单击处理程序。这样,您的视图就不需要了解附加的视图模型,也可以是纯粹的演示文稿。
答案 2 :(得分:0)
首先,您应该使用命令而不是事件。
在您当前的结构中,您必须添加
OnPropertyChanged("StudentLastName");
调用StudentViewModel中的ChangedStudent()方法。 之后,您必须将Bindings的UpdateSourceTrigger设置为PropertyChanged
Text="{Binding Path=StudentFirstName, UpdateSourceTrigger=PropertyChanged}"