The way I handle this with UserControls is my shell view model will have a reference to both the ToolbarViewModel and the DataEntryViewModel. The Toolbar will have an event that fires for each of the possible commands and the shell view model will handle the event appropriately. Here is an example:
In the MainView, I create a DataTemplate for each of the possible DataEntry views/viewmodels. Then bind a ContentPresenter to a property in my MainViewModel of a base type for the DataEntryViewModel's.
<Window
//usual window declarations>
<Window.Resources>
<DataTemplate DataType="{x:Type vm:DataEntryViewModel}">
<view:DataEntryView />
</DataTemplate>
//more DataTemplates for other data entry views
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<view:ToolbarView Grid.Row="0"
DataContext="{Binding ToolbarContext}" />
<ContentPresenter Grid.Row="1"
Content="{Binding DataEntryContext}" />
</Grid>
</Window>
The DataEntryViewModel will have a method for all the different possible actions that the toolbar may want to execute on them. I have only provided the Save method here for brevity sake.
public interface IDataEntryViewModelBase
{
void Save();
}
public abstract class DataEntryViewModelBase : ViewModelBase, IDataEntryViewModelBase
{
public virtual void Save()
{
//base save work
}
}
public class DataEntryViewModel : DataEntryViewModelBase
{
public override void Save()
{
base.Save();
//unique save work
}
}
The ToolbarViewModel has an event for each of the commands that can be executed.
public class ToolbarViewModel : ViewModelBase, IToolbarViewModel
{
//can just use EventArgs here if no need to pass data when this event fires
public event EventHandler<SaveExecutedArgs> SaveExecuted;
public ICommand CmdSave
{
get { return new RelayCommand(() => OnSaveExecuted()); }
}
protected void OnSaveExecuted()
{
var handler = SaveExecuted;
if (handler == null) return;
handler(this, new SaveExecutedArgs());
}
}
The MainViewModel has a property for the Toolbar's DataContext and one for the DataEntry DataContext. When assigning the ToolbarContext, I register a method for each command event that the Toolbar exposes. In the eventhandler, I call the appropriate method of the DataEntryContext for the event.
public class MainViewModel : ViewModelBase
{
private IToolbarViewModel _toolbarContext;
public IToolbarViewModel ToolbarContext
{
get { return _toolbarContext; }
set { Set("ToolbarContext", ref _toolbarContext, value); }
}
private IDataEntryViewModelBase _dataEntryContext;
public IDataEntryViewModelBase DataEntryContext
{
get { return _dataEntryContext; }
set { Set("DataEntryContext", ref _dataEntryContext, value); }
}
public MainViewModel()
{
_toolbarContext = new ToolbarViewModel();
_toolbarContext.SaveExecuted += ToolbarContext_SaveExecuted;
_dataEntryContext = new DataEntryViewModel();
}
private void ToolbarContext_SaveExecuted(object sender, SaveExecutedArgs e)
{
//can change this to popup a warning or however you want to handle it
//under the circumstance that DataEntryContext is null
if (DataEntryContext == null) return;
DataEntryContext.Save();
}
}