MVVM - Как я могу сослаться на дочернюю ViewModel из родительской ViewModel (ViewModel создаются в их представлениях)? - PullRequest
0 голосов
/ 19 сентября 2019

Я не знаю, как правильно создать отношение Viewmodel, если Я использую подход, согласно которому Views создает экземпляры ViewModel, а ViewModel не имеет ссылки на View.

Предположим,у нас есть элемент управления ChildView, который создает экземпляр его ViewModel с SaveCommand.

<UserControl x:Class="App.ChildView" ...>
    <UserControl.DataContext>
        <local:ChildViewModel/>
    </UserControl.DataContext>
    <!-- some controls -->
</UserControl>

public class ChildViewModel
{
    public RelayCommand SaveCommand { get; set; }
    public ChildViewModel()
    {
        SaveCommand = new RelayCommand(SaveExecute);
    }
    private void SaveExecute()
    {
         Debug.WriteLine("Child data has been saved.");
    }
}

Теперь я помещаю два элемента управления в родительское представление и хочу выполнить SaveCommand для всех дочерних элементов.

<Window x:Class="App.ParentView" ...>
    <Window.DataContext>
        <local:ParentViewModel/>
    </Window.DataContext>
    <Grid>
        <local:ChildView x:Name="child1"/>
        <local:ChildView x:Name="child2"/>
        <Button Content="Save All" Command="{Binding ParentSaveCommand}">
    <Grid/>
</Window>

public class ParentViewModel
{
    public RelayCommand ParentSaveCommand { get; set; }
    public ParentViewModel()
    {
        ParentSaveCommand = new RelayCommand(ParentSaveExecute);
    }
    private void ParentSaveExecute()
    {
        Debug.WriteLine("Saving all has started...");

        // <childVM1>.SaveCommand.Execute();
        // <childVM2>.SaveCommand.Execute();

        // From where should I get ChildViewModel?
    }
}

Как правильно я должен ссылаться на дочернюю ViewModel?

Я нашел возможные решения:

  • Добавить интерфейс к ParentView, который возвращает дочернюю ViewModel ( как AngelSix сделал с паролем )
  • Создайте класс, который сможет связывать ChildView.DataContext со свойством ParentView.DataContext.Child1ViewModel.

.

Или, может быть, это неправильный подход, и ParentViewModel должен создавать экземпляры ChildViewModel, а затем ParentView должен устанавливать DataContext для child1 и child2 (путем связывания, конечно)?

Ответы [ 2 ]

1 голос
/ 20 сентября 2019

В основном я всегда назначаю DataContext в коде, чтобы можно было вводить зависимости для просмотра конструктора модели.Мой подход заключается в передаче модели родительского представления в модели дочернего представления через конструктор.В случае, если у вас есть, я бы создал событие SaveAll в модели родительского представления и подписал на него модели дочернего представления.

Редактировать: я не использую какие-либо рамки для того, что я упомянул.Чтобы дать вам общее представление о том, как бы я работал с SaveAll в моделях с дочерним видом -

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        Child1ViewModel = new Child1ViewModel(this); // inject parent view model to child view model
        //Child2ViewModel = new Child2ViewModel(this);
    }

    public event SaveAllEventHandler SaveAll; // Child view models can subscribe to this event

    // ...
}


public class Child1ViewModel : ViewModelBase
{
    public Child1ViewModel(MainViewModel parentViewModel)
    {
        parentViewModel.SaveAll += OnSaveAll;
    }

    private void OnSaveAll(object sender, SaveAllEventArgs e)
    {
        //
    }
}

Я надеюсь, что это немного проясняет:)

Edit2: Как я уже упоминал в моем оригиналеответ, чтобы это работало, вам нужно установить DataContexts в конструкторе MainWindow в коде (а не в xaml).

1 голос
/ 20 сентября 2019

Как упоминалось в RTF, для этих типов операций обычно проще создать родительскую виртуальную машину для создания дочерних виртуальных машин, чтобы родитель мог поддерживать ссылку на дочерние виртуальные машины.Тем не менее, вы определенно можете делать то, что вы хотите без архитектурных изменений.

В представлении:

 <Button
  Content="Save All"
  Height="37" Command="{Binding SaveAllCommand}">
       <Button.CommandParameter>
            <MultiBinding Converter="{StaticResource PassThroughConverter }">
                  <Binding Path=“DataContext” ElementName="child1"/>
                  <Binding Path=“DataContext” ElementName="child2"/>
            </MultiBinding>
       </Button.CommandParameter>
 </Button>

Конвертер:

public class PassThroughConverter : IMultiValueConverter
{
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.ToList();
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

В модели родительского представления:

public class ParentViewModel
{
    public RelayCommand<object> ParentSaveCommand { get; set; }
    public ParentViewModel()
    {
        this.ParentSaveCommand = new RelayCommand<object>(obj => this.ParentSaveExecute(obj));
    }
    private void ParentSaveExecute(object items)
    {
        foreach (var item in (ChildViewModel[])items)
        {
            item.SaveCommand.Execute();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...