0

I have a MVVM application which has a WPF Grid which contains other embedded WPF Grids and at the same time, each of them contain some fields (WPF TextBlocks).

Very simplified example - View:

<Grid>

   <Grid>
       // Row definitions
       // Colum definitions
       <TextBlock Grid.Row="3" Grid.Column="0"
                  Text="{Binding Path=SomeField1}" /> 
   <Grid>

   <Grid>
       // Row definitions
       // Colum definitions
       <TextBlock Grid.Row="0" Grid.Column="1"
                  Text="{Binding Path=SomeField2}" /> 
   <Grid>

</Grid>

Each of these TextBlocks are bound to a string properties defined in view model.

View model (It implements INotifyPropertyChanged):

private string _someField1;
public string SomeField1
{
   get return _someField1;
   set 
   {
       if (_someField1 == value) return;
       _someField1 = value;
       OnPropertyChanged("SomeField1");
   }
}

private string _someField2;
public string SomeField2
{
   get return _someField2;
   set 
   {
       if (_someField2 == value) return;
       _someField2 = value;
       OnPropertyChanged("SomeField2");
   }
}

Then I have a model, I mean, a class with some public properties that is filled in by one process once data is obtained from a device. This class contains exactly the same properties as those defined in the view model.

Model:

public class MyModel
{
    private string _someField1;
    public string SomeField1
    {
       get return _someField1;
       set 
       {
           if (_someField1 == value) return;
           _someField1 = value;
       }
    }

    private string _someField2;
    public string SomeField2
    {
       get return _someField2;
       set 
       {
           if (_someField2 == value) return;
           _someField2 = value;
       }
    }
}

Later from view model I extract the data from this class (model), and I assign the values of those properties to the matching properties in view model. Finally, since view is bound to these properties, then view is correctly updated with values as below example.

View model method which extracts data received:

private void DataReceived(MyModel data)
{
    this.SomeField1= data.SomeField1;
    this.SomeField2= data.SomeField2;
}

The problem is that I have to define twice the properties, in view model and model. So I want to avoid this, I would like to bind Textblocks directly to properties in model and not defined the properties in view model to avoid redundant code. Or for example, is there any easy way to bind my model (MyModel) to the outer main grid and then textboxes bound to the properties in the view model (similar when bound itemsource in datagrid)?

Willy
  • 9,848
  • 22
  • 141
  • 284
  • I don't consider it redundant code. It appears to be because your model is very small. 'MyModel' could actually be a file or a database. At some point you have to pass the data across the view model/ model bounds usually via an interface for decoupling. When binding directly to the model you will have all the problems that the MVVM pattern was meant to solve. Now your business logic would be coupled to the view. If this isn't a problem for you (maybe your application is very small and you don't bother the extra work on changes) you can bind directly to the model. But than it's no longer MVVM. – BionicCode Jan 04 '19 at 12:23
  • Instead of setting the properties directly you could define a model interface that exposes some set and get methods. If you need to persist the data you would handle it in the model. – BionicCode Jan 04 '19 at 12:27
  • If model implements `INotifyPropertyChanged`, then things may get simpler: you don't need to create new properties in view model, rather single property with instance of model and then bind to model properties via it: `propertyWithModelInstance.ModelProperty`. Sometimes you can't change model, then for the sake of MVVM you need to provide notification and thus - create new properties. Often model properties are only read by view, for those you can use same techinque - sigle property returning instance of model. And don't call property with word *"field"* in its name ;) – Sinatr Jan 04 '19 at 12:29
  • There are dozens of questions which comply about such "typing overload" in MVVM and for sure in other patterns. Then there are either specific libraries to solve the issue (afaik Prism has simplified syntax for view model properties, I could be wrong) or common solutions, like code generation. E.g. using T4 templates to generate viewmodel from model (`partial class` with properties). – Sinatr Jan 04 '19 at 12:32
  • I read that the creator of the MVVM pattern himself considered this pattern overkill for small applications. So like always, it's your choice. But when following the pattern don't introduce a dependency between the view and the model. This is mandatory for this pattern and not an option. Once you understand the pattern and especially the purpose than it would make a perfect sense to you. Then you would've never had the idea to couple those two layers. – BionicCode Jan 04 '19 at 12:33
  • @Sinatr Please don't recommend coupling the view to the model and talk about MVVM at the same time. That's wrong. Please read about the purpose of those patterns before thinking about the implementation details. – BionicCode Jan 04 '19 at 12:41
  • @Sinatr Introducing INtoifyPropertyChanged and thus bindings to your model is a plain anti-pattern. Bindings are in fact the mechanism that introduces the dependency between view and it's data source. Bindings are declared in the view and allow the dependency to be unidirectional while pointing at the data source. But they introduce a dependency. – BionicCode Jan 04 '19 at 12:43
  • @BionicCode, it saved me a lot of time and unless you have something more severe than your opinion - I wouldn't stop ;) Oh, and btw, I am using quite some code-behind in the views, and will continue doing dirty-mvvm and using model directly and implementing `INotifyPropertyChanged` in model for [KISS](https://en.wikipedia.org/wiki/KISS_principle). Simply because I am lazy and don't like typing only for reason of something to be *pure*. – Sinatr Jan 04 '19 at 12:50
  • @Sinatr It's ok to do what you like to do. You just should know the trade-offs. But don't recommend anti-pattern to somebody who tries to follow a pattern. If this would be a big application that belongs to your company you would bring a lot of trouble to your team and maybe the company. Time is money. You have to make the distinction. Just using a command is not MVVM. Just using bindings is not MVVM. You can bind directly to your data base but then please don't call it MVVM, MVP or what ever because that's wrong and would only proof your misconception. – BionicCode Jan 04 '19 at 12:55
  • @Sinatr Bindings and commands for example are just tools or methods to help you to write code that complies with the pattern. The pattern itself (MVVM, MVP, MVC, ...) is pure structural. So that's why it matters where to put certain logic or code. – BionicCode Jan 04 '19 at 12:58
  • @BionicCode When you talk about an interface in your first comment, do you refer to create an interface for the model and then passing it to the view model contructor as a parameter? If so, would it be anti-pattern or not? – Willy Jan 04 '19 at 14:06
  • @user1624552 Let's see. MVVM dependency tree is View --> View Model --> Model. You don't have to use interfaces to decouple the model. But when doing so, e.g. to access the data model you won't depend on concrete models. When passing that interface to the view model would make the view model depend on the model or abstract model to be precise. This means View Model --> Model. Looks fine to me. Please explain yourself. What you describe here sounds like dependency injection to me. – BionicCode Jan 04 '19 at 14:22
  • @user1624552 In fact in an advanced scenario you would introduce dependency injection and therefore would have to define interfaces. – BionicCode Jan 04 '19 at 14:26
  • @BionicCode I was thinking in doing something similar to the answer proposed below by c0d3b34n but instead using a generic view model, passing it the interface. But after thinking.. the solution he proposes is the same as creating a Model Property in view model and then once I receive the data in DataReceived method to set directly the Model property and in its set method do an OnPropertyChanged("Model"); Is it ok? it seems simpler. – Willy Jan 04 '19 at 15:01
  • @BionicCode See my proposed solution below. – Willy Jan 04 '19 at 15:18
  • @BionicCode So.... if you use EF and have say 192 POCOs that represent your database, you'd rather leave the 192 POCOs as is and "re-invent the wheel" by creating another 192 INPC objects that just wrap the POCOs? Keeping in mind that each of those 192 POCOs average 10 columns? My, my, you're going to be busy creating 192 x 10 wrapper properties. Brb while I click a checkbox to have EF generate the POCOs with INPC support out of the box. Ok. Done. That was easy :). That being said, you shouldn't introduce V specific stuff in the model, of course. But binding directly to the model? Not AP at al – SledgeHammer Jan 04 '19 at 20:11
  • @SledgeHammer Sorry, I think I didn't get you. I feel like your polemic is inappropriate. Are you telling me to drop the 'VM' in 'MVVM' and bind the view directly to the model instead? In general or for this particular exception? It's not clear. What do you do if you have to add some logic or helpers to your POCOs? I wrote that there is a reason why one would like to have the model separated from the view. You must acknowledge this because this is why this patterns were invented. For example to solve problems of extensibility and testing. – BionicCode Jan 04 '19 at 21:01
  • @SledgeHammer If you don't need this separation than you can access the database directly from your view. Why bother with models and layers or architecture in general. You have requirements so satisfy them. Nobody forces you to use any pattern at all. Except when working on a professional level with a code base of multiple million lines. Here you instantly know what you get from this patterns. By the way as a smart developer you can automate a lot of things. Also file generation. And again 'MVVM' without the 'VM' is 'MV' but not 'MVVM' anymore. – BionicCode Jan 04 '19 at 21:02
  • @BionicCode You were too hasty in assuming polemic to someone who disagrees with you to actually read and understand my response I guess. Oh well. I'll explain it to you again. No, I didn't say don't use VMs. Obviously. I said wrapping 192 x 10 EF classes which already support INPC to add nothing more then double INPC support is not architecture. I would tell you what it is, but you'd just get all polemicy on me :). A VMs job is to package data in a way for the view to consume it. It's not to arbitrarly wrap things while adding zero value. – SledgeHammer Jan 04 '19 at 21:19
  • @BionicCode In the EF example, you'd still have a VM obviously since views don't connect to data directly. The VM would expose the EF classes, not re-wrap them for no reason. But to say you can't use model classes directly is a wrong assumption. The correct answer is "it depends". And again, the important point here is that Views ALWAYS bind to VMs. You told the guy he can't expose model classes in his VM which is just plain wrong. Sorry. #NoPolemic. – SledgeHammer Jan 04 '19 at 21:20
  • @BionicCode In the case of a very large database that changes often, yes, I would expose the model classes in my VM. You are not decoupling them by using an adapter, you are just hiding the dependency :). Also, you are introducing a maintenance nightmare: 192 x 10 = 1920 properties. Ok, guess what? I just changed the structure of 7 of the tables and 3 of the FK relationships and 36 properties. Good luck re-syncing the wrapper classes, cuz I'm not giving you a list of what I changed. And I'm not saying that to be rude, I mean it like in the real world... – SledgeHammer Jan 04 '19 at 22:27
  • @BionicCode I mean, if you ask the guy changing the DB to keep a strict list of every little thing he changes on a daily basis so you can keep the wrapper classes in sync, he's going to tell you to go pound sand :). He's got better things to do. Now, I'm *very* strict by the book MVVM in my code, but in the particular case of EF, I'd re-use the model classes. – SledgeHammer Jan 04 '19 at 22:29

2 Answers2

0

I would suggest a generic view model:

public class BaseViewModel<TModel>
{
    public TModel Model
    {
        get;
        private set;
    }

    public BaseViewModel(TModel model)
    {
        this.Model = model;
    }
}

Then you can bind to it:

<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Path=Model.SomeField1}" />
c0d3b34n
  • 534
  • 7
  • 14
  • And how do you notify to the view the changes in Model property? – Willy Jan 04 '19 at 14:13
  • Missing OnPropertyChanged("Model"); within private set method in Model property? – Willy Jan 04 '19 at 14:41
  • I wonder if it is not the same as doing the following instead of passing it to the view model constructor: once I receive the data in DataReceived set directly the Model property you have created in your answer and in its set method, do a OnPropertyChanged("Model"); Is it not the same and simpler? – Willy Jan 04 '19 at 14:55
  • When receiving new data, set the Model property to the new Instance and raise PropertyChanged on Model Property in ViewModel should do it: public void DataReceived(MyModel data) { this.Model = data; } – c0d3b34n Jan 04 '19 at 15:07
  • See my proposed solution. – Willy Jan 04 '19 at 15:17
  • As BionicCode commented on my proposed solution (same as yours but without using a generic view model), this is anti-pattern. It does not follow and respect MVVM pattern. When you bind to a view model property that is the Model is the same as binding to the model directly. This cause the view to depend on view model and also model. As view depends on model it breaks MVVM pattern. – Willy Jan 04 '19 at 16:28
0

I was thinking if below it is ok and respects MVVM pattern. I have thought it after seeing solution proposed by c0d3b34n. I think it is simpler and no need to do interfaces and generic view model. I have checked and it works:

Declare a property in view model:

private MyModel _model;
public MyModel Model
{
    get { return _model; }
    set   
    {
         _model = value;
         OnPropertyChanged("Model");
    }
}

Then in the view:

<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Path=Model.SomeField1}" />

... and the same for the rest of TextBlocks.

Finally:

 private void DataReceived(MyModel data)
 {
       this.Model = data;
 }

But as said by BionicCode in comments, this solution breaks MVVM pattern.

Willy
  • 9,848
  • 22
  • 141
  • 284
  • Because you asked me I will give you my opinion on this approach. Binding to a property on your view model that is the model is the same as binding to the model directly. Your view model is exposing the model as a public property to the view. To make this work your model would need to implement INotifyPropertyChanged. This should make your alarm bells ring loud. INotifyPropertyChanged is to be implememnted by the view models only as this enables binding in the WPF framework. You can do this, but it's not MVVM conform. – BionicCode Jan 04 '19 at 15:41
  • Why? The view has explicit access to the model via binding no matter if using XAML or C#. As soon as you specify the model as a binding source you created a dependency. Your example creates two dependencies for the view.: View --> Model and View --> ViewModel. – BionicCode Jan 04 '19 at 15:42
  • I highly recommend reading some article on the web like this: https://www.codeproject.com/articles/278901//Articles/278901/MVVM-Pattern-Made-Simple .After you read this you can come back with questions to help you to fully understand the pattern. This is not meant to be rude. It will really help you. Afterwards you can make that kind of design decisions by yourself. I'll promise you. – BionicCode Jan 04 '19 at 15:43
  • All patterns have a purpose. You have to understand this purpose before you can make right decisions and choices. Just google benefit of MVVM or MVP to learn what those pattern can do for you. Than decide if you need this patterns. Sometimes you have to leave your IDE and go back to school to gain and improve knowledge. Knowledge and understanding is very important but doesn't come by itself. Please read the link I provided to you. – BionicCode Jan 04 '19 at 15:47
  • When you want to use MVVM then you can't get around to bind to your view models and only the view models. Please read the link to understand why, if you didn't understood my explanation. – BionicCode Jan 04 '19 at 15:54
  • @BionicCode ok, thanks. I will read the article. Anyway, when you say I should implement Inotifypropertychanged in Model it is not necessay since my view model already implements it. So by assigning it in Datareceived method with the one passed as parameter and then doing an Onpropertychanged in the set method of view model property works. Now I understand you, since view is bound to the view model property and this property is the Model, then it makes view to depend on view model and also model and hence it breaks MVVM pattern since view should only depend on view model. – Willy Jan 04 '19 at 16:43
  • @BionicCode So solution proposed by c0d3b34n which makes use of a generic view model breaks MVVM pattern as well because in fact he bind view to a property in view model that is the Model. – Willy Jan 04 '19 at 16:44
  • Correct. If he would do it like you did in your question than the model would be hidden from the view. Only the view model would know that there is a model and how to interact with it. But he made it a public property. To make this binding work the model would need to implement INotifyPropertyChanged (which is wrong) otherwise there would be no working binding. I am repeating myself. Please read the link above. First image on top of the page is showing the dependency graph of this pattern. Scroll to bottom of the page to see the advantages of the pattern. Read everything in-between. – BionicCode Jan 04 '19 at 16:53
  • Your goal must be to be able to explain to this guy why his approach is wrong in terms of MVVM. – BionicCode Jan 04 '19 at 16:53
  • That makes sense ... thank you for explaining, @BionicCode! – c0d3b34n Jan 07 '19 at 08:11