views:

76

answers:

4

I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.

My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.

However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.

Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?

I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.

UPDATE

Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.

If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:

partial class GUI : UserControl
{
    private MainViewModel ViewModel { get; set; }

    public GUI( Model.MainModel model)
    {
        InitializeComponent();
        ViewModel = new MainViewModel( model, this.Dispatcher);
        ViewModel.Initialize();
        this.DataContext = ViewModel;
        diagnostics_toolbar.SetViewModel( ViewModel);
        user_control_in_xaml.SetHardwareConnection( model.Connection);
    }
}

where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.

Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.

You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.

A: 

Am new to MVVM myself but here's a possible solution:

Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.

Neerav
A: 

hi, if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.

so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.

blindmeis
A: 

Hi Dave,

You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.

As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have

private ICommand _getDataCommand;
public ICommand GetDataCommand
    {
        get
        {
            if (this._getDataCommand == null)
            {
                this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
            }

            return this._getDataCommand;
        }
    }


private void GetMyData{
//go and get data from Model and add to the MyControls collection
}

 private ObservableCollection<MyUserControls> _uc; 
 public ObservableCollection<MyUserControls> MyControls
            {
                get
                {
                    if (this._uc == null)
                    {
                        this._uc = new ObservableCollection<MyUserControls>();
                    }
                    return this._uc;
                }
            }

For the RelayCommand check out Josh Smiths MSDN article.

In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.

If you can't change your UC at all then you could derive a new class from it and override certain behaviour.

Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.

jameschinnock
@james thanks, I updated my original question because I think I didn't explain things very well. I added some code to give everyone an overview of what I am trying to accomplish.
Dave
Hi Dave, hmmmm...ok cool. Will have a further think. As an asside you can call a non default constructor in XAML using something like this: xmlns:UC_Stuff="clr-namespace:UserCProject" <ObjectDataProvider ObjectType="{x:Type GUI}" x:Key="MyGUI"> <ObjectDataProvider.ConstructorParameters> <UC_Stuff:GUI>Value</UC_Stuff:GUI> </ObjectDataProvider.ConstructorParameters> </ObjectDataProvider> So you could pass a binding reference in there as the Value (I think). It wouldn't be a 'good' imp of MVVM though I guess :-)
jameschinnock
Hi Dave, sorry I didn't get back to you yet. Did you have any joy at all with this?
jameschinnock
@jameschinnock sorry, haven't gotten around to it again. I have been under the gun, and progressively going farther and farther away from MVVM. It's a bit of a bastardization at the moment.
Dave
A: 

If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).

If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.


(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)


Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.

Hope that helps!

Kieren Johnstone