-2

I'm currently working on building a WPF app using MVVM and this is what I want to achieve. enter image description here

enter image description here

enter image description here

So, basically when the user clicks on an Action item, they can see the details of that item. One item can have multiple sub items, so when the user clicks on the subitem, they then can see the subItem screen. They can click on the button above the headers to go back to the previous screen. They all should use the same viewModel cs file. I am very new to WPF and C# and I have done some research but I could not find any solution for the case where they all use the same viewModel. What I found was for different views with different viewModels. I appreciate any help. Thank you so much!

May Nguyen
  • 163
  • 1
  • 7

1 Answers1

1

You can achieve this by using a single ContentControl and by changing model assigned to its Content property. The base model class looks as follows:

class NavigationViewModel : BaseViewModel
{
    public NavigationViewModel PreviousView { get; set; }
    public FrameworkElement View { get; set; }
    public RelayCommand GoBackCommand { get; private set; }
    public string Name { get; private set; } 
    public RelayCommand<NavigationViewModel> ShowSubItem { get; private set; }

    public NavigationViewModel(Action<NavigationViewModel> updateCurrentView, string name)
    {
        Name = name; 
        ShowSubItem = new RelayCommand<NavigationViewModel>(view => updateCurrentView(view));
    }  
}

Then you create specific classes for each view

HomeViewModel (with 4 action items, I created only the first one)

class HomeViewModel : NavigationViewModel
{
    public ActionItem1ViewModel Action1 { get; private set; } 

    public HomeViewModel(Action<NavigationViewModel> updateCurrentView) : base(updateCurrentView, "Home")
    { 
        Action1 = new ActionItem1ViewModel(updateCurrentView)
        {
            PreviousView = this
        };
        PreviousView = null;
        View = new HomeView(); 
    }
}

and associated view

  <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="Item #1" Command="{Binding ShowSubItem}" CommandParameter="{Binding Action1}"/>
    <Button Content="Item #2" Grid.Column="1"/>
    <Button Content="Item #3" Grid.Row="1"/>
    <Button Content="Item #4" Grid.Row="1" Grid.Column="1"/>
</Grid>

A single sub-item view model

class ActionItem1ViewModel : NavigationViewModel
{
    public Action1SubItemViewModel SubItem { get; private set; }

    public ActionItem1ViewModel(Action<NavigationViewModel> updateCurrentView) : base(updateCurrentView, "Item #1")
    {
        View = new Action1();
        SubItem = new Action1SubItemViewModel(updateCurrentView)
        {
            PreviousView = this
        };
    }
}

and associated view

 <Button Content="Item #1" Command="{Binding ShowSubItem}" CommandParameter="{Binding SubItem}"/>

and sub-item details view model

 class Action1SubItemViewModel : NavigationViewModel
{
    public Action1SubItemViewModel(Action<NavigationViewModel> updateCurrentView) : base(updateCurrentView, "Sub Item details")
    {
        View = new SubItem();
    }
}

and associated view

 <TextBlock Text="details"/>

The MainWindow looks as follows

 <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="{Binding CurrentView.PreviousView.Name}" Command="{Binding GoBackCommand}">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Visibility" Value="Visible"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding CurrentView.PreviousView}" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>
    <ContentControl Grid.Row="1" Content="{Binding View}" DataContext="{Binding CurrentView}" Margin="50"/>
</Grid>

and its view model

 class MainViewModel : BaseViewModel
{
    private NavigationViewModel _currentView;
    public NavigationViewModel CurrentView
    {
        get => _currentView;
        set
        {
            _currentView = value;
            OnPropertyChanged();
        }
    }

    public RelayCommand GoBackCommand { get; private set; }

    public MainViewModel()
    {
        CurrentView = new HomeViewModel(UpdateCurrentView);
        GoBackCommand = new RelayCommand(() =>
        {
            CurrentView = CurrentView.PreviousView;
        });
    }

    private void UpdateCurrentView(NavigationViewModel viewModel)
    {
        CurrentView = viewModel;
    }
}

enter image description here

Maximus
  • 3,458
  • 3
  • 16
  • 27
  • thank you so much for your help. it's very details and helpful. But I have a question, what if I want to use only 1 view model which is NavigationViewModel for all of them, should it be the same with the solution you gave or I should follow some other practice? – May Nguyen Apr 12 '23 at 15:10
  • 1
    Each view has a separate ViewModel because views differ. If you put information for all views in one ViewModel then you can use solely the NavigationViewModel class but this is not advisable. Some separation is welcome. – Maximus Apr 12 '23 at 16:00