0
votes

I am working on a WPF application that consists of a lot of data entry forms. I have exposed each of these forms as a separate user control. So, I have CustomerView (UserControl), CompaniesView (UserControl). Each form consist of fields like Customer Name, Company Name that user enters and then saves them.

The save action is performed using a Toolbar which consists of "Save", "Delete" and "Close" options. I have created Toolbar into a separate UserControl and put that in the container view/ shell view. Here is the structure of things that will make it clear.

CONTAINER BEGINS
--------------------------------------------------------------------

-----------------------------------
THIS IS THE TOOLBAR
----------------------------------
______________________________________


Data Entry forms are injected here on the fly


______________________________________


---------------------------------------------------------------------
CONTAINER ENDS

The problem is that when I click the Save button in the Toolbar I have no idea of knowing anything about the view which contains the form. I need to get hold of the MVVM model attached to that view so I can save it.

1
Are you using Prism? Typically, I implement IActiveAware in my viewmodel and the region manager automatically sets IsActive on my ViewModel and View. I then create a "SaveCommand" which is a CompositeCommand and register each ViewModel's command with it. The ViewModel toggles its commands IsActive property when it is active, and therefore only the correct one is called.Alan
Yes I am using Prism. Although I am new to Prism and WPF hence my knowledge is limited. I will surely check it out.john doe

1 Answers

0
votes

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();
   }
}