如何在子视图模型中导航内容展示器?

How to navigate the Content Presenter in the Child View Model?

目标

我的实际目标是导航 ContentPresenter 不是通过主导航,而是通过导航页面中的按钮。

我目前的成绩

这是我左侧的主导航:

单击任一主要导航项时,ContentPresenter 将加载它的 ViewModel。

这是 Home 选项卡

Some Other tab

预期结果

我的期望是从加载的视图模型中单击按钮(见下图),然后导航到另一个视图模型...

但我不确定如何实现这样的想法。

代码

页面视图模型

public class PageViewModel
{
    public string Title { get; set; }
    public object Content { get; set; }
    public List<PageViewModel> Children { get; set; }
}

主视图模型

public class MainViewModel
{
    public List<PageViewModel> Navigation { get; set; }
    public MainViewModel()
    {
        Navigation = new List<PageViewModel>
        {
            new PageViewModel
            {
                Title = "Home",
                Content = new HomeViewModel()
            },
            new PageViewModel
            {
                Title = "Some Other Tab",
                Content = new SomeOtherViewModel()
            }
        };
    }
}

MainWindow.xaml

...
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel>
            <ListView ItemsSource="{Binding Navigation}"
                      x:Name="Nav">
                <ListView.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:PageViewModel}">
                        <TextBlock Text="{Binding Title}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>

        <ContentPresenter Content="{Binding ElementName=Nav, Path=SelectedItem.Content}" 
                          Grid.Column="1"/>

    </Grid>
</Window>

主视图模型

public class HomeViewModel
{
    public string SomeTitle { get; set; }
    public HomeViewModel()
    {
        SomeTitle = "Hello Home ViewModel";
    }
}

一些其他视图模型

public class SomeOtherViewModel
{
    public string SomeTitle { get; set; }
    public SomeOtherViewModel()
    {
        SomeTitle = "Hello SomeOther View Model";
    }
}

问题

通过内部(子)视图模型导航的正确实现是什么?

您必须在 MainViewModel 中实现 INotifyPropertyChanged 并在 listview 中添加一个名为 SelectedItembind 的 属性。

我把执行此操作的代码放在下面。代码运行正常。

PageViewModel.cs

public class PageViewModel
{
   public string Title { get; set; }
   public object Content { get; set; }
}

MainViewModel.cs

public class MainViewModel : INotifyPropertyChanged
{
    public List<PageViewModel> Navigation { get; set; }
    private PageViewModel selectedItem { get; set; }
    public PageViewModel SelectedItem
    {
       get { return selectedItem; }
       set
       {
           selectedItem = value;
           OnPropertyChanged("SelectedItem");
       }
    }

    public MainViewModel()
    {
       Navigation = new List<PageViewModel>
       {
            new PageViewModel
            {
                Title = "Home",
                Content = new HomeViewModel(this),
            },
            new PageViewModel
            {
                Title = "Some Other Tab",
                Content = new SomeOtherViewModel(),
            }
        };
        SelectedItem = Navigation.FirstOrDefault();
    }

    private void OnPropertyChanged(string propertyName)
    {

        if (PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

HomeViewModel.cs

public class HomeViewModel
{
    public string SomeTitle { get; set; }
    public object View { get; set; }
    MainViewModel mainViewModel;
    public RelayCommand SomeOtherCommand { get; private set; }
       
   public HomeViewModel(MainViewModel _mainViewModel)
    {
       SomeTitle = "Hello Home ViewModel";
       View = new View1(this);
       mainViewModel = _mainViewModel;
       SomeOtherCommand = new RelayCommand(SomeOtherMethod);
    }
    private void SomeOtherMethod(object parameter)
    {
       mainViewModel.SelectedItem = mainViewModel.Navigation.Where(a => a.Title == "Some Other Tab").FirstOrDefault();
    }
}

SomeOtherViewModel.cs

public class SomeOtherViewModel
{
    public string SomeTitle { get; set; }
    public object View { get; set; }
    public SomeOtherViewModel()
    {
       SomeTitle = "Hello SomeOther View Model";
       View = new View2();
    }
}

RelayCommand.cs

public class RelayCommand : ICommand
{
   readonly Action<object> _execute;
   readonly Predicate<object> _canExecute;

   public RelayCommand(Action<object> execute, Predicate<object> canExecute)
   {
     if (execute == null)
     {
        throw new NullReferenceException("execute");
     }
     else
     {
        _execute = execute;
        _canExecute = canExecute;
     }
   }

   public event EventHandler CanExecuteChanged;
   public void RaiseCanExecuteChanged()
   {
      if (CanExecuteChanged != null)
         CanExecuteChanged(this, EventArgs.Empty);
   }

   public RelayCommand(Action<object> execute) : this(execute, null)
   {

   }
   public bool CanExecute(object parameter)
   {
      return _canExecute == null ? true : _canExecute(parameter);
   }

   public void Execute(object parameter)
   {
      _execute.Invoke(parameter);
   }
}

MainWindow.xaml

<Window.DataContext>
  <local:MainViewModel />
</Window.DataContext>
<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>

  <StackPanel>
    <ListView ItemsSource="{Binding Navigation}" x:Name="Nav" SelectedItem="{Binding Path=SelectedItem,Mode=TwoWay}">
      <ListView.ItemTemplate>
        <DataTemplate DataType="{x:Type local:PageViewModel}">
          <TextBlock Text="{Binding Title}" />
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackPanel>
  <ContentPresenter Content="{Binding ElementName=Nav, Path=SelectedItem.Content.View}" Grid.Column="1" />
</Grid>

View1.xaml

<Grid>
  <Button x:Name="btnGet" Content="get" Height="40" Command="{Binding SomeOtherCommand}"></Button>
</Grid>

View2.xaml

<Grid>
  <Button Content="test"></Button>
</Grid>

View1.cs

public partial class View1 : UserControl
{
   public View1(HomeViewModel homeViewModel)
   {
      InitializeComponent();
      DataContext = homeViewModel;
   }
}