使用WPF和MVVM模式将值从子窗口传递到父窗口

我有父窗口,其中包含名为“SchoolName”的textBox和一个名为“Lookup school Name”的按钮。

该按钮打开一个包含学校名称列表的子窗口。 现在,当用户从子窗口中选择学校名称,并单击“使用所选学校”按钮。 我需要在父视图的文本框中填充选定的学校。

注意:我已采用Sam和其他人的建议来使这段代码有效。 我更新了我的代码,以便其他人可以简单地使用它。

SelectSchoolView.xaml(父窗口)

   

SchoolNameLookup.xaml(查找学校名称的子窗口)

        

SchoolNameLookupViewModel

  private string _schoolNames; public string SchoolNames { get { return _schoolNames; } set { _schoolNames= value; OnPropertyChanged(SchoolNames); } } private ICommand _useSelectedSchoolNameCommand; public ICommand UseSelectedSchoolNameCommand{ get { if (_useSelectedSchoolNameCommand== null) _useSelectedSchoolNameCommand= new RelayCommand(a => DoUseSelectedSchollNameItem(), p => true); return _useSelectedSchoolNameCommand; } set { _useSelectedSchoolNameCommand= value; } } private void DoUseSelectedSchoolNameItem() { StringBuilder sfiString = new StringBuilder(); ObservableCollection oCol = new ObservableCollection(); foreach (SchoolModel itm in SchollNameList) { if (itm.isSelected) { sfiString.Append(itm.SchoolName + "; "); _schoolNames = sfiString.ToString(); } } OnPropertyChanged(SchoolNames); } private ICommand _displayLookupDialogCommand; public ICommand DisplayLookupDialogCommand { get { if (_displayLookupDialogCommand== null) _displayLookupDialogCommand= new RelayCommand(a => DoDisplayLookupDialog(), p => true); return _displayLookupDialogCommand; } set { _displayLookupDialogCommand= value; } } private void DoDisplayLookupDialog() { SchoolNameLookup snl = new SchoolNameLookup(); snl.DataContext = this; //==> This what I was missing. Now my code works as I was expecting snl.Show(); } 

我的解决方案是将两个窗口绑定到同一个ViewModel,然后定义一个属性来保存代码的结果值,让我们称之为CurrentSchoolCodes,将标签绑定到此属性。 确保CurrentSchoolCodes引发INotifyPropertyChanged事件。 然后在DoUseSelectedSchoolNameItem中设置CurrentSchoolCodes的值。

对于模型中的属性,我建议您根据需要加载它们(Lazy Load patttern)。 我这个方法你的属性的get访问器检查相关字段是否仍为空,加载并赋值给它。 代码就像这段代码:

 private ObservableCollection _schoolList; public ObservableCollection SchoolList{ get { if ( _schoolList == null ) _schoolList = LoadSchoolList(); return _schoolList; } } 

这样,第一次绑定到此SchoolList属性的WPF控件尝试获取此属性的值时,将加载并缓存该值,然后返回该值。

注意:我不得不说应该谨慎使用这种属性,因为加载数据可能是一个耗时的过程。 最好在后台线程中加载数据以保持UI响应。

Sam在这里提出的解决方案是正确的。 你没有得到的是你应该只有一个viewmodel实例,你的主页和子页应该引用同一个。 你的viewmodel应该实例化一次:也许你需要一个定位器并在那里得到实例…这样做你的ctor中的代码会触发一次,看看mvvmLight工具包,我认为它对你的使用很有帮助,你可以摆脱那些实现ICommand的类……你可以在这里找到一个使用该模式的好例子: http : //blogs.msdn.com/b/kylemc/archive/2011/04/29/mvvm-pattern -for-ria-services.aspx基本上会发生什么:

你有一个定位器

 public class ViewModelLocator { private readonly ServiceProviderBase _sp; public ViewModelLocator() { _sp = ServiceProviderBase.Instance; // 1 VM for all places that use it. Just an option Book = new BookViewModel(_sp.PageConductor, _sp.BookDataService); } public BookViewModel Book { get; set; } //get { return new BookViewModel(_sp.PageConductor, _sp.BookDataService); } // 1 new instance per View public CheckoutViewModel Checkout { get { return new CheckoutViewModel(_sp.PageConductor, _sp.BookDataService); } } } 

该定位器是一个StaticResource,在App.xaml中

      

在您的视图中,您通过定位器引用视图模型:

  DataContext="{Binding Book, Source={StaticResource Locator}}" 

这里Book是BookViewModel的一个实例,你可以在Locator类中看到它

BookViewModel有一个SelectedBook:

  private Book _selectedBook; public Book SelectedBook { get { return _selectedBook; } set { _selectedBook = value; RaisePropertyChanged("SelectedBook"); } } 

并且您的子窗口应该与MainView具有相同的DataContext,并且工作方式如下: