none
MVVM: How to bind a page in a tab control with a viewmodel?

    Question

  • We practise MVVM pattern.
    We have a main window which contains tab controls. Each tabItem contains a page.
    Since the main window needs to share some data with one of page, so our MainWindowViewModel creates the instance a RunconditionViewModel.
    Then we need to set the instance of RunconditionViewModel to one of page's DataContent (page called ControlPage.xaml).
    How do we achieve that? see the code below.

    In HDMainWindow.xaml,
    <TabControl x:Name="tabControlMain" DockPanel.Dock="Left" MinHeight="200"  SelectedIndex="0" TabStripPlacement="Left" >
    <TabItem Header="Run" Name="TabItemControlPage"> <Frame Name="FrameControlPage" Source="Features\Basic Control\ControlPage.xaml"></Frame> <!--<Frame Source="Features\Basic Control\LoginPage.xaml"></Frame>--> </TabItem> in App.xaml.cs,

    protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); InitializeComponent(); HDMainWindow hd = new HDMainWindow(); HDMainWindowViewModel hwd = new HDMainWindowViewModel(); hd.DataContext = hwd; //if (hd.TabItemControlPage != null) // hd.TabItemControlPage.DataContext = hwd.RunConditionsViewModel; if (hd.FrameControlPage != null) hd.FrameControlPage.DataContext = hwd.RunConditionsViewModel; hd.Show(); }
    someshow through hd.FrameControlPage we can not find the page control. So we set the DataContext in the FrameControlPage.
    However, all binding the ControlPage.xaml does not work. ControlPage.xaml Header secton define as following:


    <Page x:Class="HD_Prototype.ControlPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:HD_Prototype" Title="MainPage" Name="MainControlPage">
    How can we find the MainCotrolPage control through the mainwindow?
    How can we set the MainControlPage.DataContent to RunConditionViewModel instance through the MainWindowViewModel?
    Thx!
    • Edited by JJChen Thursday, May 14, 2009 7:03 PM
    Wednesday, May 13, 2009 10:38 PM

Answers

  • My coworkder Matt gives me some suggestions and it solves the problem.

    1) To implement MVVM pattern, viewmodol classes should mutual exclusive each other.

    2) If two viewmodel classes will share some data, the data should come from model and duplicate in two viewmodel classes

    3) Make the model class singleton class in that way all shared data come from one place and share between viewmodel classes.

    Since we implement HDMainViewModel and RunCondtionsViewModel separetly,
    when creating page view, we create RunConditionViewModel and bind it to page.DataContext.

    • Marked as answer by Zhi-Xin Ye Thursday, May 21, 2009 10:24 AM
    Thursday, May 14, 2009 7:34 PM

All replies

  • My coworkder Matt gives me some suggestions and it solves the problem.

    1) To implement MVVM pattern, viewmodol classes should mutual exclusive each other.

    2) If two viewmodel classes will share some data, the data should come from model and duplicate in two viewmodel classes

    3) Make the model class singleton class in that way all shared data come from one place and share between viewmodel classes.

    Since we implement HDMainViewModel and RunCondtionsViewModel separetly,
    when creating page view, we create RunConditionViewModel and bind it to page.DataContext.

    • Marked as answer by Zhi-Xin Ye Thursday, May 21, 2009 10:24 AM
    Thursday, May 14, 2009 7:34 PM
  • Interesting, just reading this now (a year later)...

    Does anyone know why we would store or why we should store the Data in the Model?  If we have a Model which is the Data Accessor for an SQL database, we get the data, and plug the Viewmodel with the DataTable or Dataview.  The Model then can be marked for GC.  If we maintain a copy of this in the Model aren't we duplicating storage needs as well as violating the "one time, one place" principal for OOP?

    To me if a ViewModel already contains the Data for View A,if View B needs the same data, then a singleton pattern of the ViewModel seems more logical to me being that the data has already been displayed in a view somewhere.  This allows for XAML Binding on View B to the same content that's on View A. 

    As far as testing goes this reduces testing to one model, on viewmodel... Instead of two viewmodels and one model.


    Javaman
    Wednesday, September 22, 2010 6:36 PM
  • I would not recommend that approach, Javaman.

     

    First of all, singleton is really not a pretty pattern, there are better alternatives.

    Ideally, you have multiple Views (each with their own ViewModel, and each of those with their own Model).

    The data that they access would be the "static" source you're looking for. If all ViewModels retrieve their data from the same source, then no matter how many views you had, the data displayed would be the same, and in sync.

     

    -Catch.

    Wednesday, September 22, 2010 6:55 PM
  • Agreed, data should be shared via the models not the via the VMs.
    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.

    My blog: http://www.RyanVice.net/
    Wednesday, September 22, 2010 7:01 PM
  • Not sure if there was a misunderstanding, but a a data source is where the data should be stored. And that data is static and shared amongst your application. Which tier of your application retrieves the data is up to you, but I choose to retrieve it in the ViewModel. The Model is simply a set of properties for the most part, the ViewModel is where the interaction is.

     

    So View = UI

    ViewModel = Backend to UI - interaction with data source, propagate changes to Model

    Model = stateless set of properties that the ViewModel is in sync with

     

    -Catch.

    Wednesday, September 22, 2010 7:08 PM
  • That is one approach but I prefer to have the model contain all the business logic so that if I wanted to write an ASP.Net version of my application I wouldn't have to duplicate the BL that is contained within the VMs for fetching data from the persistence store and moving that data to the the model.

    Example

    // In options VM

    OnGetOptionsCommand()
    {

        Options = this.model.GetOptions(this.OptionSymbol);

    }

     

    If I instead fetched the options directly in the VM instead of indirectly (Model calls to service layer) then when I introduce my APS.Net version I'd have to rewrite that code. I'd at a minimum put that code in a seperate service layer.

    Example

    // In options VM

    OnGetOptionsCommand()
    {

        this.Services.Options.GetOptions(this.OptionSymbol); // Updates Model
        Options = this.model.Options;

    }  

     


    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.

    My blog: http://www.RyanVice.net/
    Wednesday, September 22, 2010 7:24 PM
  • In current design I have View--->ViewModel--->Model--->Sends and Gets SQL Queries to SQL Database.

    The SQL database is the static storage.  So the Model (in this case) is a configurable query (front end) that just interfaces with SQL Database.  It (the Model) returns a DataView that is directly bindable to the ViewModel property of the same type.  The View binds to that Viewmodel property in the XAML only.  Question:  When we set the ViewModel property to the value of the Dataview from the Model this is done by reference right?  I seem to remember that certain objects are always reference and not value.

    Someone mentioned earlier, that we may want different views... no problem, because the fact that the datagrid binds directly to a dataview all that has to be done is to change the DataView.Rowfilter to any new view wanted. There's no traversing the Model to the SQL server, thus it sets up true ADO disconnected mode ability.  And it's about twice as fast as reissuing a query.  So in the case of another model just to change the view, I think a better option is to set up a Command to just change the view...  Everything is still loosely coupled this way... With exception of course of Model updating a ViewModel Property.


    Javaman
    Wednesday, September 22, 2010 7:36 PM
  • Sorry, I may have not been clear with my descriptions. The model's are shared in our implmentation for the reasons you just described. The idea being that each view has a VM that converts the model data to a WPF consumable form but the model is implemented in a way that data can be shared across views when it makes sense to do so.
    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.

    My blog: http://www.RyanVice.net/
    Wednesday, September 22, 2010 7:40 PM
  • Ryan,

     

    When you say shared across Views, do you mean directly or indirectly?

    Wednesday, September 22, 2010 7:51 PM
  • Ok Ryan, we are on same page.  But still I don't see any issue with deferring DataViews to the Viewmodel. The reason I like this is that we could have any number of commands (each in it's own class) bound to the view.  When the user wants to switch a view of data, the command object shouldn't know anything concerning the View controls, rather it should only alter the data view itself.  If the command object alters the dataview and the Viewmodel is updated as a result of the command, viola, the view changes.  If we apply this same logic to a Model object then we have everything that alters the view doing it the same way...  I'm treating the ViewModels as the anchor to the changing the views.
    Javaman
    Wednesday, September 22, 2010 7:56 PM
  • Well there's a lot of options on how this could be done but the point was that if you want to share data across views then I think a good approach is to shared your model as opposed to sharing at the VM level. You could do this via dependency injection of model into VM, by having a service layer that implements caching of models, implementing models as singletons, etc...


    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.

    My blog: http://www.RyanVice.net/
    Wednesday, September 22, 2010 8:28 PM
  • In our application, we use SQL server compact edition to store our data.

    We use entity framework to model our data (model layer) into entity objects which maps from our SQL tables.

    We actually need to implement Data Access layer which is singleton class to access those entity data models.

    Data Access layer provides all functions such as Load entity collection, update/delete entity objects and save change back to SQL tables (through entity framework).

    ViewModel layer will call DataAcess layer to access those entity collections.

    Most cases view and viewmodel is one-to-one relationship.

    In some special cases, two views are very similar. we use one ViewModel to support two views by using some flags to distinguish which view to support. Jane

     

     

    Thursday, September 23, 2010 9:21 PM
  • Depending on the complexity of your application you may want to consider adding a business logic layer between the VM and the DAL which would be where your models would live. The VM is not the best place for business logic if you follow the pattern as the model should be transferable to other client implementation (ASP.Net, WinForm, CommandLine, etc...). The idea being that your model is an independent software asset outside of the world of WPF while the VM and V are WPF specific.
    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.

    My blog: http://www.RyanVice.net/
    Friday, September 24, 2010 12:18 PM