Passing value from child window to parent window u

2019-02-19 20:50发布

I have parent window which has textBox called "SchoolName", and a button called "Lookup school Name".

That Button opens a child window with list of school names. Now when user selects school Name from child window, and clicks on "Use selected school" button. I need to populate selected school in parent view's textbox.

Note: I have adopted Sam’s and other people’s suggestion to make this code work. I have updated my code so other people can simply use it.

SelectSchoolView.xaml (Parent Window)

<Window x:Class="MyProject.UI.SelectSchoolView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Parent" Height="202" Width="547">
<Grid>
    <TextBox Height="23" Width="192" 
     Name="txtSchoolNames"  
     Text="{Binding Path=SchoolNames, UpdateSourceTrigger=PropertyChanged, 
     Mode=TwoWay}" 
     />

    <Label Content="School Codes" Height="28" HorizontalAlignment="Left" 
     Margin="30,38,0,0" Name="label1" VerticalAlignment="Top" />
    <Button Content="Lookup School Code" Height="30" HorizontalAlignment="Left" 
     Margin="321,36,0,0" Name="button1" VerticalAlignment="Top" Width="163" 
     Command="{Binding Path=DisplayLookupDialogCommand}"/>
</Grid>
</Window>

SchoolNameLookup.xaml (Child Window for Look up School Name)

<Window x:Class="MyProject.UI.SchoolNameLookup"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
    Title="SchoolCodeLookup" Height="335" Width="426">

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="226*" />
        <RowDefinition Height="70*" />
    </Grid.RowDefinitions>

    <toolkit:DataGrid Grid.Row="0" Grid.Column="1"   x:Name="dgSchoolList" 
                          ItemsSource="{Binding Path=SchoolList}" 
                          SelectedItem="{Binding Path=SelectedSchoolItem, Mode=TwoWay}" 
                          Width="294"
                          AutoGenerateColumns="False"
                          CanUserAddRows="False" 
                          CanUserDeleteRows="False"
                          CanUserResizeRows="False" 
                          CanUserSortColumns="True" 
                          SelectionMode="Single">

        <Button Grid.Row="1" Grid.Column="1" Content="Use Selected School Name" 
         Height="23" Name="btnSelect" Width="131" Command="{Binding 
         Path=UseSelectedSchoolNameCommand}"  />
 </Grid>
</Window>

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<SchoolModel> oCol = 
                new ObservableCollection<SchoolModel>();
        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();
    }

2条回答
Lonely孤独者°
2楼-- · 2019-02-19 21:06

The Solution Sam suggested here is a correct one. What you didn't get is that you should have only one instance of you viewmodel and your main and child page should refer to the same one. Your viewmodel should be instanciated once: maybe you need a Locator and get the instance there... Doing like this the code in your ctor will fire once, have a look at the mvvmLight toolkit, I think it will be great for your usage, you can get rid of those Classes implementing ICommand too... You can find a great example of using that pattern here: http://blogs.msdn.com/b/kylemc/archive/2011/04/29/mvvm-pattern-for-ria-services.aspx basically what happens is this:

you have a Locator

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); }
    }
}

that Locator is a StaticResource, in App.xaml

<Application.Resources>
        <ResourceDictionary>
        <app:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
        </ResourceDictionary>
    </Application.Resources>

in your views you refer you viewmodels trough the Locator:

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

here Book is an instance of BookViewModel, you can see it in the Locator class

BookViewModel has a SelectedBook:

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

and your child window should have the same DataContext as your MainView and work like this:

<Grid Name="grid1" DataContext="{Binding SelectedBook}"> 
查看更多
淡お忘
3楼-- · 2019-02-19 21:20

My solution is to bind both the windows to the same ViewModel, then define a property to hold the resulting value for codes, lets call it CurrentSchoolCodes, Bind the label to this property. Make sure that CurrentSchoolCodes raises the INotifyPropertyChanged event. then in the DoUseSelectedSchoolNameItem set the value for CurrentSchoolCodes.

For properties in your models I suggest you to load them as they are required(Lazy Load patttern). I this method your property's get accessor checks if the related field is still null, loads and assigns the value to it. The code would be like this code snippet:

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

In this way the first time your WPF control which is binded to this SchoolList property tries to get the value for this property the value will be loaded and cached and then returned.

Note: I have to say that this kind of properties should be used carefully, since loading data could be a time consuming process. And it is better to load data in a background thread to keep UI responsive.

查看更多
登录 后发表回答