I have a WPF app which has two User Controls. On one user control, I have a list of buttons which are all binded to a command that changes the ViewModel from that user control to the other, with a command parameter of the button's content. This works just fine. If I click any of the buttons, the command successfully runs and the ViewModel changes to the other user control with the button's content being passed through. However, if I try and execute the command in the ViewModel's constructor (after the command has been instantiated), it does not work. When I set a breakpoint and follow it, it appears to change the ViewModel, but the view does not appear to change in the window. I cannot figure out what the difference is between executing the command programmatically compared to with a button.
I am new to MVVM, so please let me know if I am doing something incorrectly or if you need more info.
ViewModel 1 (where I create the buttons and try to execute command programmatically):
public class SelectProgramViewModel : ViewModelBase
{
public List<string> ProgramNames { get; set; }
public ObservableCollection<Button> ProgramButtons { get; set; }
public SettingsObject SettingsObject { get; set; }
public PersonalSettings PersonalSettings { get; set; }
public ICommand SelectProgramCommand { get; }
public SelectProgramViewModel(MappingStore mappingStore, NavigationStore navigationStore)
{
NavigationService<ParsingViewModel> navigationService = new NavigationService<ParsingViewModel>(
navigationStore,
() => new ParsingViewModel(mappingStore, navigationStore));
SelectProgramCommand = new SelectProgramCommand(this, mappingStore, navigationService);
// Retrieve settings information
const string settingsObjectPath = "I:\\Next Phase\\Logistics\\Applications\\WM Parser\\Resources\\SettingsObject.json";
SettingsObject = JsonConvert.DeserializeObject<SettingsObject>(File.ReadAllText(settingsObjectPath));
PersonalSettings = JsonConvert.DeserializeObject<PersonalSettings>(File.ReadAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WM Parser", "PersonalSettings.json")));
// Create program buttons
ProgramNames = SettingsObject.ProgramMappings.OrderBy(x => x.Name).Select(x => x.Name).ToList();
ProgramButtons = new ObservableCollection<Button>();
foreach (var programName in ProgramNames)
{
ProgramButtons.Add(
new Button()
{
Content = programName,
Command = SelectProgramCommand,
CommandParameter = programName,
Margin = new Thickness(5, 5, 5, 5)
});
}
// Check if last used program exists
if (PersonalSettings.LastUsedProgramName != null &&
SettingsObject.ProgramMappings.Select(x => x.Name).Contains(PersonalSettings.LastUsedProgramName))
{
SelectProgramCommand.Execute(PersonalSettings.LastUsedProgramName);
}
}
}
SelectProgramCommand:
public class SelectProgramCommand : CommandBase
{
private readonly SelectProgramViewModel _viewModel;
private readonly MappingStore _programStore;
private readonly NavigationService<ParsingViewModel> _navigationService;
public SelectProgramCommand(SelectProgramViewModel viewModel, MappingStore programStore, NavigationService<ParsingViewModel> navigationService)
{
_viewModel = viewModel;
_programStore = programStore;
_navigationService = navigationService;
}
public override void Execute(object parameter)
{
_programStore.NovaMapping = _viewModel.SettingsObject.NovaMapping;
_programStore.ParsingMapping = _viewModel.SettingsObject.ParsingMapping;
_programStore.SelectedProgram = _viewModel.SettingsObject.ProgramMappings.First(x => x.Name == parameter.ToString());
_navigationService.Navigate();
}
}
CommandBase (which SelectProgramCommand inherits):
public abstract class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
protected void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
NavigationService:
public class NavigationService<TViewModel>
where TViewModel : ViewModelBase
{
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;
public NavigationService(NavigationStore navigationStore, Func<TViewModel> createViewModel)
{
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}
public void Navigate()
{
_navigationStore.CurrentViewModel = _createViewModel();
}
}
NavigationStore:
public class NavigationStore
{
public event Action CurrentViewModelChanged;
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get => _currentViewModel;
set
{
_currentViewModel = value;
OnCurrentViewModelChanged();
}
}
private void OnCurrentViewModelChanged()
{
CurrentViewModelChanged?.Invoke();
}
}