4

I need create a Property in one of my user controls of a Model type but i think i must prevent direct access to Model Layer from View Layer.

I have a View Model of the Model that provide set of my model objects...

  • SetOfA_UserControl
  • SetOfA_ViewModel
  • A_Model

I need a property Like this in my user control:

public A_Model SelectedA { get; set; }

One way is create a new View Model like following codes and use it in my User Control:

// ------------ View Model Layer ------------

public class SingleA_ViewModel: ModelA
{
}

// --------------- View Layer ---------------

public SingleA_ViewModel SelectedA { get; set; }

But I'm trying to prevent a new empty view model class that inherit the model like above. is it correct?

What is your suggestions to prevent direct access to Model layer and create the Property in my User Control???


EDIT 1:

I have 3 project:

  • View Project
  • View Model Project
  • Model Project

I want know can i prevent reference to Model project in View project or not....

I have a SelectedA property in my View Model too and i put my logic in View model class and it work well in my view but also i have a SelectedA Property in my UserControl that i bind it to SelectedA property in my ViewModel class... But i need direct access to Model in UserControl to define this property!

When i have a direct access to Model from View my codes is like this:

// ------------ Model Layer ------------
public class AModel
{
}

// ------------ View Model Layer ------------
public class SetOfA_ViewModel: INotifyPropertyChanged
{
    public AModel SelectedA { get; set; }

    public ObservableCollection<AModel> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            OnPropertyChanged("Items");
        }
    }

    // Other Logic codes to fill and keep SelectedA value and....
}



// --------------- View Layer ---------------
public partial class MyUserControl : UserControl
{
    public AModel SelectedA { 
    get { return (AModel)GetValue(SelectedAProperty); }
        set
        {
            var oldValue = (AModel)GetValue(SelectedAProperty);
            if (oldValue != value) SetValue(SelectedAProperty, value);
        }
    }

    public static readonly DependencyProperty SelectedAProperty =
        DependencyProperty.Register(
            "SelectedA",
            typeof(AModel),
            typeof(MyUserControl),
            new PropertyMetadata(OnSelectedAValueChanged));

    public MyUserControl ()
    {
        InitializeComponent();

        const string NAME_OF_PROPERTY_IN_VM = "SelectedA";
        var binding = new Binding(NAME_OF_PROPERTY_IN_VM) { Mode = BindingMode.TwoWay };
        this.SetBinding(SelectedAProperty, binding);
    }

    private static void OnSelectedAValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        //------
    }  
}

The above method work well for me and i use this now BUT I'm trying to Delete any direct access and reference to Model Project in my View Project then how can i implement the SelectedA dependency property of AModel in my user control?

Some friends say you can access model project directly from view project. i want know Correct ways, NOT possible ways...!


Edit 2

When i keep SelectedA item in my user control then use it in my window like this:

<userControls:MyUserControl x:Name="MyUserControl1"/>

<Label Content="{Binding ElementName=MyUserControl1, Path=SelectedA.Title}" />

Edit 3

Why i want prevent direct access to Model from ViewModel?

I searched about MVVM diagrams and did not find and direct access from View to Model. all the diagrams says:

enter image description here

..........enter image description here

Now can we direct access to model from view yet?

  • Why there are many samples that have direct access to model in View on the web?

  • And why some people say you can do this?

  • If we can do this and direct access to model is a correct implement why there is not any relation between View and Model in the above diagrams???

Emond
  • 50,210
  • 11
  • 84
  • 115
Ramin Bateni
  • 16,499
  • 9
  • 69
  • 98
  • *but i think i must prevent direct access to Model Layer from View Layer* well, you're 100% wrong on that, so no need to worry. –  May 29 '15 at 14:10
  • @Will do you say i can access to `Model Layer` from `View Layer` Directly in `MVVM`??? May you give me a reliable reference to this fact? – Ramin Bateni May 29 '15 at 14:18
  • @RAM MVVM is a pattern, it isn't the law. Generally, your **ViewModel** should expose the **Model** to the **View** via a **Property** in your ViewModel. – Mike Eason May 29 '15 at 14:26
  • i have seen many codes where the view binds to the model. I understand it works, but it breaks the MVVM design. I mean, i understand it should not be done (like you said). That said, may you post your view, model-view, and model code? I am a little bit confused. Are you using the code behind? – heringer May 29 '15 at 14:26
  • Show me a reference that demands the view know nothing of the model! Considering the view is *binding to the model*, and the view *knows the PROPERTIES!!! of the model*, how can you possibly believe that the view know nothing of the model? It's preposterous, and your question shows why the belief should be considered harmful. –  May 29 '15 at 15:02
  • 1
    The viewmodel is the abstraction of the model for viewing purposes. Models are not designed for viewing purposes. Ofc you can bind to the models directly but then you are throwing all the benefits out of the window you gain by doing the extra work of MVVM. Please don't do it, just stick to your controllers if you don;t get it. – MrDosu May 29 '15 at 16:22
  • @Will, i Edited my question and put two image and some questions in it about MVVM relations between Model, View and ViewModel. now what do you think? Also what do you think about Erno.de.Weerd answer? He recommend `viewmodel` between `view` and `model`. – Ramin Bateni May 29 '15 at 16:23
  • @RAM - Please do not ask for discussions, those are off topic on Stack Overflow. http://stackoverflow.com/help/dont-ask – Emond May 30 '15 at 07:51
  • @MikeEason Re "VM expose model to view via a property" - doing so breaks VM's role as the mediator (binder) between V and M. I'm midway through a week *undoing* all such "breaking" in order to make an app easier to maintain across multiple platforms. The problem (with allowing direct access) is that (in a complex app) it becomes non-obvious how the view can - and should - interact with the model. An acceptable compromise is to define an *interface* that is all the view is allowed to know. But it isn't much harder to simply add the handful of needed methods (per model) to each VM. – ToolmakerSteve Dec 21 '17 at 22:05
  • @MikeEason - Modifying what I said above. It is convenient for some views to hold "tokens" referring to models being acted on. I have empty interfaces on some models, to act as "tokens", so views can pass around these typesafe references, and pass them to methods in VMs to act on them. (Each interface is known to correspond to a single class, so VM can direct cast to that class.) This way I know that *all* manipulation of models is done by VMs, not by Views. A single point of responsibility for a maintenance coder to discover what model features are accessed. – ToolmakerSteve Dec 21 '17 at 22:26
  • NOTE: [I've added an answer](https://stackoverflow.com/a/47934191/199364) showing code for my approach. – ToolmakerSteve Dec 21 '17 at 23:58

3 Answers3

5

A viewmodel is not a model so it should not derive.

If you decide to couple the view to the model, any change in the model which is often dictated by an external source, might affect the views it is used in.

When using a viewmodel for each view some views might not be affected at all by the change and affected views can be fixed by either adjusting the view or by writing code in the viewmodel.

Yes, adding an extra layer in between is extra work but it also comes with a clear point of transitioning from view to model and back again. After a couple of increments it might be worth it.

The extra layer also provides a nice point of extension for commands and validation and view specific properties.

If you decide to expose the model as a property of the viewmodel it can become very tempting to add properties and commands to the model that are view specific. They will pollute the model quickly and make the model hard to be reused or regenerated.

There is no law and there is no police. Take my arguments into consideration and pick an option. Try to be open to change your design later. What appears to be easy right now might become difficult later on. We don't know what will happen; be agile/flexible. In my experience I found that for applications that survive many versions it was more efficient to have a clean separation between model and viewmodel but for short lived apps it might be too much.

When implementing the MVVM pattern I always make sure that the model doesn't know or assume anything about the viewmodel and the viewmodel doesn't know or assume anything about the view. The viewmodel is the man in the middle; it knows where to get instances of the model and where to send changes of the model. many times I have used one or more repositories that know how to get or save model instances so the viewmodel only has to know about the repository. The repository can take care or delegate other features such as caching across viewmodel instances.

In general I create one viewmodel per view and I assign an instance of the viewmodel to the datacontext of the view. All binding paths (for both properties and commands) are relative to that viewmodel.

Sometimes I nest viewmodels by adding properties to the main viewmodel that are viewmodels themselves.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • but Mr Will says to me that i think incorrect about preventing direct access to model from view please read his descriptions in comments of my question! – Ramin Bateni May 29 '15 at 15:41
  • @RAM - I added to my answer. – Emond May 29 '15 at 16:56
  • @EmodeWeerd Thank u, So do you recommend me to create a new class in my `ViewModel Project` and re-implement some of `Model Properties` that are usefull in `View` in it and use it in my `View`? All of my logic codes in `Viewmodel` and `service` and.... plays with some object of `Model` Class type, now i should re-implement a class in `ViewModel` layer like my `Model` class to be used in view... it can be very useful if you provide a simple sample include codes of `MVVM` classes of your recommendations. Please remember that i want have a property in my view to keep all properties of my model . – Ramin Bateni May 29 '15 at 21:01
  • @RAM - On StackOverflow we like to have one question per topic. Just create a new question so others will see it and will chime in too. Be careful though, asking "give me the code" or asking too vague advice is not on topic. Read http://stackoverflow.com/help/dont-ask and http://stackoverflow.com/help/on-topic – Emond May 30 '15 at 07:39
  • @RAM - I added some extra, generic tips, on how I implement MVVM – Emond May 30 '15 at 07:49
0

"Blind Interfaces to Models":

Here is how I avoid having View know about Model, yet give Views a way to pass around knowledge as to what model is being worked on.
Common scenario: on one page a user picks an item, app goes to another page which wants to know which item was picked.

My approach here is a "middle ground": It allows views to reason about, and communicate, model state, without exposing anything other than a "blind" interface identifying the model objects. This is possible because the viewmodels understand the models, so can be queried when information is needed.

Before inventing this, we allowed View classes to know model classes. Which became a PITA when porting to new platforms - complex logic tended to be written in View classes. That code had to be identified and re-created in each new platform - it was a tangle of UI-specific and model-specific reasoning. We had ViewModel classes with most of the model-specific reasoning, but it still was too easy to "lose" some subtle logic when porting. Now, the model-specific reasoning at minimum requires calling into the VM. And it becomes obvious when code should be moved from V into the VM (the code has many VM references).

NOTE: it includes "IOwner", (or "IView" might be better name) which is a way for VM to get info from the view, such as contents of a textbox. Perhaps this goes away with proper use of data binding, but I haven't mastered that yet.

This didn't start with a "textbook" implementation of MVVM. Rather, it started with tangled code on one platform, as I moved more and more model details from View classes into (newly added) VM classes, until the Views no longer knew any details of the Models. So the View is forced to call VM for any manipulation of the model. [Once I fully grok "data binding", I can probably make this code cleaner. I'm not necessarily recommending this approach, but if you need to migrate existing non-MVVM, non-data-binding, code, you can get here step-by-step via refactoring, rather than rewriting from scratch.]

// ---------- Views ----------

// TODO: Replace with your platform-specific View base class.
internal class View
{

}

// Each View class has access to its ViewModel class, and sometimes to other View classes.
// If a View1 needs to alter a ViewModel belonging to another View2, this should be via custom methods on View2,
// or perhaps via a limited Interface onto ViewModel2.
// E.g., to build the initial state of a View2 that we are segueing to.
internal class View1 : View, VM1.IOwner
{
    private VM1 vm;

    // --- Implementing method of "VM1.IOwner" ---
    // TODO: Replace this with access to a textbox in your UI.
    private string _text1;
    public string Text1
    {
        get { return _text1; }
        set { _text1 = value; }
    }

    // TODO: Replace these with platform-specific methods for page appearing, disappearing.
    public void OnLoad()
    {
        vm = new VM1(this);
        vm.Load();
    }

    public void OnStore()
    {
        vm.Store();
    }

    // Example of "building" the input to a following view.
    public void OnSegue(View nextView)
    {
        var nextView2 = nextView as View2;
        if (nextView2 != null)
            nextView2.IncomingA = vm.CurrentA;
    }


    // --- represents a method used within view's workflow. ---
    public void SomeMethod()
    {
        // E.g., based on user selection, make a specified ModelA be "current".
        // (In practice, it would take a more complex scenario for view to want to hold an IModelA.)
        // (In this simple case, View could hold an index, call SetCurrentModelAByIndex, not need an IModelA object.)
        IModelA desiredA = vm.GetOneModelA(1);
        if (desiredA != null)
            vm.CurrentA = desiredA;
        else
        {
            // ... message to user ...
        }
    }
}


internal class View2 : View //TODO , VM2.IOwner
{
    internal IModelA IncomingA;
    // ...
}


// ---------- ViewModels ----------
// Each of these corresponds to a single view. So "VM1" corresponds to "V1".
// ViewModel classes have access to Model classes.

internal class VM1
{
    // This gives a limited way for VM1 to "call back" to its V1.
    // "IView" would be an alternative name for this.
    internal interface IOwner
    {
        string Text1 { get; set; }
    }

    private readonly IOwner _owner;

    private ModelA _a;

    private IList<ModelA> someAs;

    internal VM1(IOwner owner)
    {
        _owner = owner;
    }

    internal void Load()
    {
        // TODO: Replace this with logic that is told (or "knows") where to get the active Models.
        someAs = new List<ModelA> {new ModelA(), new ModelA()};

        _a = (someAs.Count > 0 ? someAs[0] : null);
        _owner.Text1 = (_a != null ? _a.TextA : "No ModelA");
    }

    internal void Store()
    {
        // Called by V1 to store user input back to model(s).
        if (_a != null)
            _a.TextA = _owner.Text1;
    }


    // View is only allowed to know about "IModelA", not "ModelA".
    internal IModelA GetOneModelA(int index)
    {
        if (index < someAs.Count)
            return someAs[index];
        else
            return null;
    }

    // Return "true" if succeeds.
    internal bool SetCurrentModelAByIndex(int index)
    {
        IModelA desiredA = GetOneModelA(index);
        if (desiredA != null)
        {
            CurrentA = desiredA;
            return true;
        }
        else
            return false;
    }

    internal IModelA CurrentA
    {
        get
        {
            // Note the return type is "IModelA"; this represents the ModelA without giving view direct access to features of A.
            return _a;
        }
        set
        {
            // By design, IModelA always holds a ModelA (or null), so this cast always succeeds.
            _a = (ModelA)value;
        }
    }


    // Shows how view can get value of a specified field of CurrentA.
    internal string CurrentTextA()
    {
        return TextA(_a);
    }

    // Shows how view can get value of a specified field of any ModelA.
    internal string TextA(IModelA ia)
    {
        var a = (ModelA)ia;
        // TBD: Or maybe pass an empty string when missing.
        return (a != null ? a.TextA : null);
    }
}


// ---------- "Blind" Model Interfaces ----------

// This has NO methods. It is used to pass around a "ModelA" WITHOUT exposing its features or class.
// It is all that a "View" is allowed to know; must be passed to a VM to act on it.
internal interface IModelA
{
}


// ---------- Models ----------

internal class ModelA : IModelA
{
    internal string TextA;
}
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
-1

If you want a SelectedA which represents a SelectedItem then this should be in the ViewModel as more likely then not you will want to access this in your business logic layer.

Expose you're property public A_Model SelectedA { get; set; } in the ViewModel (implement INotifyPropertyChanged and then bind the SelectedItem of whatever collection control you are using to this property.

If this is used as purely for the UI, then bind directly to the control in question in XAML.

Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • exactly i have a `SelectedA` property in my `View Model` too and i put my logic in `view model` and it work well in my view but also i have a `SelectedA` Property in my `UserControl` that i bind it to `SelectedA` property in my `ViewModel class`... But i need direct access to `Model` in `UserControl` to define this property! – Ramin Bateni May 29 '15 at 14:38
  • Why do you also have a property in the UserControl? If something this probably should be just be of type `object` and let WPF do the conversion when you are binding to ViewModel. Otherwise there is no problem using Models in the View, its mainly you shouldn't be using View specific in Models/ViewModel. – Michal Ciechan May 29 '15 at 15:05
  • i added two Edit section in my question to explain more... i also wrote about your question of me... – Ramin Bateni May 29 '15 at 15:27