2

Have started learning UWP development by creating a Windows Template Studio application with Horizontal Navigation project type, MVVM Light design pattern and two blank pages (MainPage and StatsPage).

In the MainPage I have a list of items and I want to show the selected item's statistics in the StatsPage when the user hits the relevant NavigationViewItem. In my wpf apps, I would set up a RelayCommand with command parameter on a button in the Main view, and in the viewmodel, call the command's method with the param/arg to open the Stats view with the correct info. In the created Template Studio app, the NavigationViewItem in the ShellPage calls OnItemInvoked() in its VM which doesn't know about the selection in the MainPage view.

<winui:NavigationView.MenuItems>
  <winui:NavigationViewItem x:Uid="Shell_Main" helpers:NavHelper.NavigateTo="LG_Ess.ViewModels.MainViewModel" />
  <winui:NavigationViewItem x:Uid="Shell_Stats" helpers:NavHelper.NavigateTo="LG_Ess.ViewModels.StatsViewModel" />
</winui:NavigationView.MenuItems>
  <i:Interaction.Behaviors>
    <ic:EventTriggerBehavior EventName="ItemInvoked">
      <ic:InvokeCommandAction Command="{x:Bind ViewModel.ItemInvokedCommand}" />
    </ic:EventTriggerBehavior>
  </i:Interaction.Behaviors>

How do I pass the MainPage selected item as a param to StatsPage via the ShellPage's NavigationViewItem? I could probably work it out by adding a button to my MainPage and hiding the Shell navigation, but I'd prefer to do it in the Template Studio style.

Further investigations show that the ShellViewModel seems to be the default DataContext for all pages created by the Template Studio. In my pages I have set the Data Context to the autogenerated View Models by adding:

<Page.DataContext>
  <local:{PageName}ViewModel/>
</Page.DataContext>

So, it seems I can either have a single ViewModel and treat the pages as UserControls, or map the DataContext for each page to its own ViewModel, and do some ViewModelLocator dancing to access properties from one VM in another.

mcalex
  • 6,628
  • 5
  • 50
  • 80

1 Answers1

0

So, it seems I can either have a single ViewModel and treat the pages as UserControls, or map the DataContext for each page to its own ViewModel, and do some ViewModelLocator dancing to access properties from one VM in another.

Correct. You could define a 'SelectedStats' relevant property in your MainViewModel and make your ListView's SelectedItem bind to this property and set Mode=TwoWay. Then, on your 'StatsPage', you could use EventTriggerBehavior to bind a 'LoadedCommand' in StatsViewModel like the 'ShellPage'. In StatsViewModel, you could get the selected item by calling ViewModelLocator.Current.MainViewModel.SelectedStats.

See my simple code sample:

<!--MainPage.xaml-->
<ListView ItemsSource="{x:Bind ViewModel.list}" SelectedItem="{x:Bind ViewModel.SelectedStats,Mode=TwoWay}">
</ListView>
public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        list = new ObservableCollection<string>();
        list.Add("string1");
        list.Add("string2");
        list.Add("string3");
    }

    public ObservableCollection<string> list { get; set; }

    private object _SelectedStats;

    public object SelectedStats
    {
        get { return _SelectedStats; }
        set
        {
            if (_SelectedStats != value)
            {
                _SelectedStats = value;
                RaisePropertyChanged("SelectedStats");
            }
        }
    }
}
<!--StatsPage.xaml-->
<i:Interaction.Behaviors>
    <ic:EventTriggerBehavior EventName="Loaded">
        <ic:InvokeCommandAction Command="{x:Bind ViewModel.LoadedCommand}" />
    </ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
public class StatsViewModel : ViewModelBase
{
    public StatsViewModel()
    {
    }

    private ICommand _LoadedCommand;
    public ICommand LoadedCommand => _LoadedCommand ?? (_LoadedCommand = new RelayCommand(LoaedAsync));

    private async void LoaedAsync()
    {
        var selectedObject = ViewModelLocator.Current.MainViewModel.SelectedStats;
        //TODO:...
        await Task.CompletedTask;
    }
}
Xie Steven
  • 8,544
  • 1
  • 9
  • 23