please try the next solution:
Xaml (is based on data template selector)
<Window x:Class="MvvmNavigationIssue.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mvvmNavigationIssue="clr-namespace:MvvmNavigationIssue"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<mvvmNavigationIssue:MainNavigationViewModel/>
</Window.DataContext>
<Window.Resources>
<mvvmNavigationIssue:FreezableProxyClass x:Key="ProxyElement"
ProxiedDataContext="{Binding Source={x:Reference This}, Path=DataContext}"/>
<DataTemplate x:Key="DefaultDataTemplate">
<Grid>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Tomato" />
<TextBlock Text="Default Template" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="JobsDataTemplate">
<ListView ItemsSource="{Binding JobModels, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="mvvmNavigationIssue:JobModel">
<TextBlock Text="{Binding Id}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="Salary" DisplayMemberBinding="{Binding Salary, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="mvvmNavigationIssue:JobModel">
<Button Command="{Binding Source={StaticResource ProxyElement},
Path=ProxiedDataContext.ShowLogsCommand, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding }">Logs</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
<DataTemplate x:Key="LogsDataTemplate">
<ListView ItemsSource="{Binding LogModels, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="mvvmNavigationIssue:JobModel">
<TextBlock Text="{Binding Id}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="Time" DisplayMemberBinding="{Binding LogTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="Event" DisplayMemberBinding="{Binding LogEvent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<GridViewColumn Header="" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="mvvmNavigationIssue:JobModel">
<Button Command="{Binding Source={StaticResource ProxyElement},
Path=ProxiedDataContext.ShowAllJobsCommand, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}">All Jobs</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
<mvvmNavigationIssue:MainContentTemplateSelector x:Key="MainContentTemplateSelectorKey"
DefaultDataTemplate="{StaticResource DefaultDataTemplate}"
JobsViewDataTemplate="{StaticResource JobsDataTemplate}"
LogsViewDataTemplate="{StaticResource LogsDataTemplate}"/>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentViewModel, UpdateSourceTrigger=PropertyChanged}"
ContentTemplateSelector="{StaticResource MainContentTemplateSelectorKey}"></ContentControl>
</Grid>
MVVM code
public class FreezableProxyClass : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new FreezableProxyClass();
}
public static readonly DependencyProperty ProxiedDataContextProperty = DependencyProperty.Register(
"ProxiedDataContext", typeof(object), typeof(FreezableProxyClass), new PropertyMetadata(default(object)));
public object ProxiedDataContext
{
get { return (object)GetValue(ProxiedDataContextProperty); }
set { SetValue(ProxiedDataContextProperty, value); }
}
}
public class MainNavigationViewModel : BaseObservableObject
{
private object _currentViewModel;
private JobsViewModel _jobsViewModel;
private List<LogModel> _logModels;
private ICommand _showLogs;
private ICommand _showJobs;
public MainNavigationViewModel()
{
_jobsViewModel = new JobsViewModel();
Init();
}
private void Init()
{
_jobsViewModel.JobModels = new ObservableCollection<JobModel>
{
new JobModel{Id = 1, Salary = "12k", Title = "Hw Engineer"},
new JobModel{Id=2, Salary = "18k", Title = "Sw Engineer"},
new JobModel{Id = 3, Salary = "12k", Title = "IT Engineer"},
new JobModel{Id=4, Salary = "18k", Title = "QA Engineer"},
};
_logModels = new List<LogModel>
{
new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"},
new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"},
new LogModel{Id = 1, Salary = "12k", Title = "Hw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"},
new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"},
new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"},
new LogModel{Id=2, Salary = "12k", Title = "Sw Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"},
new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"},
new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"},
new LogModel{Id = 3, Salary = "12k", Title = "IT Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"},
new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Pending"},
new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Active"},
new LogModel{Id=4, Salary = "12k", Title = "QA Engineer", LogTime = DateTime.Now.ToLocalTime(), LogEvent = "Closed"},
};
CurrentViewModel = _jobsViewModel;
}
public object CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
OnPropertyChanged(()=>CurrentViewModel);
}
}
public ICommand ShowLogsCommand
{
get { return _showLogs ?? (_showLogs = new RelayCommand<JobModel>(ShowLogs)); }
}
private void ShowLogs(JobModel obj)
{
CurrentViewModel = new LogsViewModel
{
LogModels = new ObservableCollection<LogModel>(_logModels.Where(model => model.Id == obj.Id)),
};
}
public ICommand ShowAllJobsCommand
{
get { return _showJobs ?? (_showJobs = new RelayCommand(ShowAllJobs)); }
}
private void ShowAllJobs()
{
CurrentViewModel = _jobsViewModel;
}
}
public class LogsViewModel:BaseObservableObject
{
private ObservableCollection<LogModel> _logModels;
public ObservableCollection<LogModel> LogModels
{
get { return _logModels; }
set
{
_logModels = value;
OnPropertyChanged();
}
}
}
public class LogModel : JobModel
{
private DateTime _logTime;
private string _logEvent;
public DateTime LogTime
{
get { return _logTime; }
set
{
_logTime = value;
OnPropertyChanged();
}
}
public string LogEvent
{
get { return _logEvent; }
set
{
_logEvent = value;
OnPropertyChanged();
}
}
}
public class JobsViewModel:BaseObservableObject
{
private ObservableCollection<JobModel> _jobModels;
public ObservableCollection<JobModel> JobModels
{
get { return _jobModels; }
set
{
_jobModels = value;
OnPropertyChanged();
}
}
}
public class JobModel:BaseObservableObject
{
private int _id;
private string _title;
private string _salary;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged();
}
}
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged();
}
}
public string Salary
{
get { return _salary; }
set
{
_salary = value;
OnPropertyChanged();
}
}
}
INPC implementation and Relay command code
/// <summary>
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
public class RelayCommand : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action _execute;
public RelayCommand(Action execute)
: this(() => true, execute)
{
}
public RelayCommand(Func<bool> canExecute, Action execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter = null)
{
return _canExecute();
}
public void Execute(object parameter = null)
{
_execute();
}
public event EventHandler CanExecuteChanged;
}
public class RelayCommand<T> : ICommand
where T:class
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public RelayCommand(Action<T> execute):this(obj => true, execute)
{
}
public RelayCommand(Predicate<T> canExecute, Action<T> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter as T);
}
public void Execute(object parameter)
{
_execute(parameter as T);
}
public event EventHandler CanExecuteChanged;
}
Small explanation
- We have three DataTemplate Jobs, Logs, Default.
- The DataTemplateSelector will manage the data template selection based on the Content property of the ContentControl which the DataTemplateSelector is attached to it.
- The button is inside the grid and it is bound to the command from the parent's VM. The parent VM is provided to the DataTemplate via Freezable proxie object.
Let me know if you have problems with the code.
Regards.