Передача значения из дочернего окна в родительское окно с использованием шаблона WPF и MVVM - PullRequest
2 голосов
/ 22 сентября 2011

У меня есть родительское окно, в котором есть текстовое поле с именем «SchoolName» и кнопка «Поиск имени школы».

Эта кнопка открывает дочернее окно со списком названий школ. Теперь, когда пользователь выбирает название школы из дочернего окна и нажимает кнопку «Использовать выбранную школу». Мне нужно заполнить выбранную школу в текстовом поле родительского представления.

Примечание. Я принял предложение Сэма и других людей, чтобы этот код работал. Я обновил свой код, чтобы другие могли его просто использовать.

SelectSchoolView.xaml (родительское окно)

<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 (дочернее окно для поиска названия школы)

<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 ]

1 голос
/ 22 сентября 2011

Решение, которое Сэм предложил здесь, является правильным. Чего вы не получили, так это того, что у вас должен быть только один экземпляр вашей модели представления, а ваша главная и дочерняя страницы должны ссылаться на один и тот же. Ваша view-модель должна быть запущена один раз: может быть, вам нужен Locator и получить экземпляр там ... Делая так, код в вашем 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

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

в ваших представлениях вы ссылаетесь на модели представления через локатор:

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

здесь Book - это экземпляр BookViewModel, вы можете увидеть его в классе Locator

BookViewModel имеет выбранную книгу:

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

и ваше дочернее окно должно иметь тот же DataContext, что и ваш MainView, и работать так:

<Grid Name="grid1" DataContext="{Binding SelectedBook}"> 
1 голос
/ 22 сентября 2011

Мое решение состоит в том, чтобы связать оба окна с одной и той же ViewModel, затем определить свойство для хранения результирующего значения для кодов, давайте назовем его CurrentSchoolCodes, Привязать метку к этому свойству.Убедитесь, что CurrentSchoolCodes вызывает событие INotifyPropertyChanged.затем в DoUseSelectedSchoolNameItem установите значение для CurrentSchoolCodes.

Для свойств в ваших моделях я предлагаю вам загружать их по мере необходимости (Lazy Load patttern).В этом методе метод доступа get вашего свойства проверяет, является ли связанное поле пустым, загружает и присваивает ему значение.Код будет похож на этот фрагмент кода:

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

Таким образом, когда ваш первый элемент управления WPF, связанный с этим свойством SchoolList, пытается получить значение для этого свойства, оно будет загружено и кэшировано, изатем вернулся.

Примечание: я должен сказать, что этот тип свойств должен использоваться осторожно, так как загрузка данных может быть длительным процессом.И лучше загружать данные в фоновом потоке, чтобы пользовательский интерфейс реагировал.

...