关于mvvm页面导航的两个问题

Posted

技术标签:

【中文标题】关于mvvm页面导航的两个问题【英文标题】:Two questions about mvvm navigation of pages 【发布时间】:2019-11-05 22:06:09 【问题描述】:

我正在尝试制作从 EN 到其他语言的模板翻译器 (.doc)。

这只是给我的。

我已经完成了简单的 mvvm 导航。为了清楚地了解我想要什么,请查看图片:

第一个问题:如何将 ICommand 从按钮“NextItem”翻译到当前选定的已更改文本框内项目的页面,否则如何从当前页面为我的 Button 调用 Translate() 方法哪个在 MainView 中?

第二个问题:如何将所有页面放在上侧窗口的组合框中的窗口中,然后从那里选择页面,就像我用按钮一样。 p>

现在怎么样了:

<Button
    x:Name="ButtonSecondView"
    Width="200"
    Command="Binding GoToSecondViewCommand"
    Content="SecondView" />

<Button
    x:Name="ButtonNextItem"
    Grid.Row="2"
    Width="250"
    Command="Binding NextRandomItem"
    Content="Next item" />

MyCollection 只是一个生成随机项(1 项、3 项等...)的存根。 在那里我可以在页面初始化时将一些参数转换为页面。

public MainViewModel()

    MyCollection = new MyCollection();
    CurrentViewModel = new FirstViewModel(this,MyCollection.GetRandomItem());
    PageList = MyCollection.GetList();


public ICommand GoToFirstViewCommand
    
        get
        
            return new RelayCommand(() =>  CurrentViewModel = new FirstViewModel(this, MyCollection.GetRandomItem()); );
        
    

public ICommand GoToSecondViewCommand
    
        get
        
            return new RelayCommand(() =>  CurrentViewModel = new SecondViewModel(this, MyCollection.GetRandomItem()); );
        
    

SecondViewModel 中的ctor

public SecondViewModel(INotifyContentChanged contentChanged,string Parametrs)

    ContentChanged = contentChanged;
    TextContent = Parametrs;

再来一次:第一个问题。

我有很多页面(其中 3 个),我需要点击底部的按钮,在我的页面中。在我当前的页面中,我从 textBox 获取文本,并将这些参数输入到我的方法中,例如 Translate(string field1)。这适用于我想要的所有页面。如果我更改选择组合框项目的页面,我可以执行相同的按钮单击按钮,并在我的方法 Translate(string field1) 中输入来自 textBox 的文本。

【问题讨论】:

宁可这样在主ViewModel中绑定单个命令,并根据索引从mina视图中的按钮传递索引返回所需的VM。 拜托,你能试着改写你的第一个问题吗?我会在我的回答中解决这个问题。现在我不明白意思。 谢谢。我更新了我的答案 【参考方案1】:

为了导航并将参数传递给相应的页面视图模型,我坚持您的模式并使用了组合。我介绍了一个组合容器,它将所有页面视图模型保存在 Dictionary&lt;string, IPageViewModel&gt; 中。因此所有的页面视图模型都必须实现这个接口。作为键,我使用了页面视图模型的类型名称(例如nameof(FirstViewModel))。我还引入了一个名为PageNavigationParameter 的新属性,它绑定到TextBox 以获取内容(应该传递给相应的页面视图模型)。

第二个Dictionary&lt;string, string&gt; 将每个页面视图模型的显示名称(将在ComboBox 中显示的页面名称)映射到实际的页面视图模型名称(与类名称匹配,例如nameof(FistViewModel))。这样,您可以通过类名或在导航范围内从页面显示名称获取所需的页面视图模型。

要从ComboBox 中选择页面,您可以这样做:

    在视图模型中创建页面名称集合并将其绑定到ComboBox.ItemSourceComboBox.SelectedItem 属性绑定到视图模型 当视图模型的属性改变时导航到页面

要使这个示例工作,您需要一个所有页面视图模型都必须实现的通用接口(例如class FirstViewModel : IPageViewModel)。此接口必须至少包含PageNavigationParameter

页面视图模型界面

interface IPageViewModel

  string PageNavigationParameter  get; set; 

主视图模型(使用合成)

class MainViewModel

    public MainViewModel()
    
      // The Dictionary to get the page view model name 
      // that maps to a page display name
      this.PageViewModelNameMap = new Dictionary<string, string>()
      
         "First Page", nameof(FirstViewModel),
         "Second Page", nameof(SecondViewModel)
      ;

      // The ComboBox's items source
      // that holds the page view model display names
      this.PageNames = new ObservableCollection<string>(this.PageViewModelNameMap.Keys);

      // The Dictionary that stores all page view models 
      // that can be retrieved by the page view model type name
      this.PageViewModels = new Dictionary<string, IPageViewModel>()
      
         nameof(FirstViewModel), new FirstViewModel(),
         nameof(SecondViewModel), new SecondViewModel()
      ;

      this.CurrentPageViewModel = this.PageViewModels[nameof(FirstViewModel)];
      this.PageNavigationParameter = string.Empty;
    

    // You can use this method as execute handler 
    // for your NavigateToPage command too
    private void NavigateToPage(object parameter)
    
      if (!(parameter is string pageName))
      
        return;
      

      if (this.PageViewModelNameMap.TryGetValue(pageName, out string pageViewModelName)
      
        if (this.PageViewModels.TryGetValue(pageViewModelName, out IPageViewModel pageViewModel)
        
          pageViewModel.PageNavigationParameter = this.PageNavigationParameter;
          this CurrentPageViewModel = pageViewModel;
        
      
    

    private bool CanExecuteNavigation(object parameter) => parameter is string destinationPageName && this.PageViewModelNameMap.Contains(destinationPageName);

    private void OnSelectedPageChanged(string selectedPageName)
    
      NavigateToPage(selectedPageName);
    

    private ObservableCollection<string> pageNames;   
    public ObservableCollection<string> PageNames
    
      get => this.pageNames;
      set 
       
        this.pageNames = value; 
        OnPropertyChanged();
      
    

    private string selectedPageName;   
    public string SelectedPageName
    
      get => this.selectedPageName;
      set 
       
        this.selectedPageName = value; 
        OnPropertyChanged();
        OnSelectedPageChanged(value);
       
     

    private string pageNavigationParameter;   
    public string PageNavigationParameter

    
      get => this.pageNavigationParameter;
      set 
       
        this.pageNavigationParameter= value; 
        OnPropertyChanged();
      
    

    private Dictionary<string, ViewModelBase> pageViewModels;   
    public Dictionary<string, ViewModelBase> PageViewModels
    
      get => this.pageViewModels;
      set 
       
        this.pageViewModels = value; 
        OnPropertyChanged();
      
    

    private Dictionary<string, string> pageViewModelNameMap;   
    public Dictionary<string, string> PageViewModelNameMap
    
      get => this.pageViewModelNameMap;
      set 
       
        this.pageViewModelNameMap = value; 
        OnPropertyChanged();
      
    

    private IPageViewModel currentPageViewModel;   
    public IPageViewModel CurrentPageViewModel 
    
      get => this.currentPageViewModel;
      set 
       
        this.currentPageViewModel= value; 
        OnPropertyChanged();
       
     
  

具有跨页面范围的控件必须将MainViewModel 作为其DataContext

XAML sn-p

<!-- The page menu (DataContext is MainViewModel) -->
<ComboBox SelectedItem="Binding SelectedPageName" ItemsSource="Binding PageNames" />

<!-- The navigation parameter TextBox (DataContext is MainViewModel) -->
<TextBox Text="Binding PageNavigationParameter" />

对于您的导航按钮命令,您可以使用相同的 MainViewModel.NavigateToPage() 方法作为执行委托处理程序和 CanExecuteNavigation 作为可以执行处理程序。所以你现在有一个导航命令(例如NavigateToPage),它通过将页面显示名称传递为CommandParameter来导航到目标页面。

【讨论】:

以上是关于关于mvvm页面导航的两个问题的主要内容,如果未能解决你的问题,请参考以下文章

Android简易音乐重构MVVM Java版-使用Navigation导航组件重构主界面及其他页面跳转(二十)

页面导航后 WPF MVVM 数据绑定中断

在 Xamarin.Forms 中使用 MVVM 进行页面导航

如何在 MVVM 模式中从页面导航到 WPF 中的页面?没有棱镜的概念[重复]

导航到其他页面 IocContainers 和 MVVM light

导航到页面与视图