2

I'm trying to bind a ContentControl's Content to a UserControl I have instantiated in my ViewModel. I cannot use the method with binding to a ViewModel and then have the UserControl be the DataTemplate of the ViewModel, as I need the Content of the ContentControl to be able to change frequently, using the same instance of the UserControls/Views, and not instantiate the views each time i re-bind.

However, when setting the UserControl-property to a UserControl-instance, and then when the view is rendered/data-bound I get: Must disconnect specified child from current parent Visual before attaching to new parent Visual. Even though I have not previously added this UserControl to anywhere, I just created this instance earlier and kept it in memory.

Are there a better way to achieve what I am doing?

In the ViewModel

public class MyViewModel : INotifyPropertyChanged
{
    //...

    private void LoadApps()
    {
        var instances = new List<UserControl>
                          {
                              new Instance1View(),
                              new Instance2View(),
                              new Instance3View(),
                          };
        SwitchInstances(instances);
    }

    private void SwitchInstances(List<UserControl> instances)
    {
        CenterApp = instances[0];
    }

    //...

    private UserControl _centerApp;
    public UserControl CenterApp
    {
        get { return _centerApp; }

        set
        {
            if (_centerApp == value)
            {
                return;
            }

            _centerApp = value;
            OnPropertyChanged("CenterApp");
        }
    }

    //...
}

In the View.xaml

<ContentControl Content="{Binding CenterApp}"></ContentControl>
cederlof
  • 7,206
  • 4
  • 45
  • 62
  • "a UserControl I have instantiated in my ViewModel" You're doing it wrong. Instantiate a secondary view model in your primary view model, bind the content control to that, and use a content template to display its visual tree. VMs should not be creating views (or depending on them). – Kent Boogaart Mar 28 '13 at 11:25
  • @KentBoogaart As I understand (and say in my question): _I cannot use the method with binding to a ViewModel and then have the UserControl be the DataTemplate of the ViewModel, as I need the Content of the ContentControl to be able to change frequently, using the same instance of the UserControls/Views, and not instantiate the views each time i re-bind._ Or am I not understanding how the datatemplate works? – cederlof Mar 28 '13 at 11:38

1 Answers1

4

Too long for a comment.

Building up on what @Kent stated in your comment, The whole point of MVVM is to disconnect the view-model from view related stuff(controls) which blocks the testing capability of GUI applications. Thus you having a UserControl / Button / whatever graphical view-related item negates the entire principle of MVVM.

You should if using MVVM comply with its standards and then re-address your problem.

  1. With MVVM you normally have 1 view <-> 1 view-model
  2. View knows about its View Model(Normally through DataContext). Reverse should not be coded into.
  3. You try to put logic controlling the view in the view-model to allow testing logic(Commands and INPC properties)

... and quite a few more. It's pretty specific in the extents of view-model not having view related stuff for eg not even having properties in view-model like Visibility. You normally hold a bool and then in the view use a converter to switch it to the Visibility object.

Reading up a bit more into MVVM would certainly help you,

Now for something to address your current issue:

Following a MVVM structure,

your going to have ViewModels such as

  • Main: MyViewModel
  • Derive all instance ViewModels from a base to allow them being kept in a list.
  • List or hold individually Instance1ViewModel, Instance2ViewModel, Instance3ViewModel in MyViewModel (Either create it yourself or if your using an IOC container let it inject it)
  • Have MyViewModel expose a property just like your posted example:

Example:

// ViewModelBase is the base class for all instance View Models
private ViewModelBase _currentFrame;
public ViewModelBase CurrentFrame {
  get {
    return _currentFrame;
  }

  private set {
    if (value == _currentFrame)
      return;
    _currentFrame = value;
    OnPropertyChanged(() => CurrentFrame);
  }
}
  • Now in your MyView.xaml View file you should(does'nt have to be top level) set the top-level DataContext to your MyViewModel
  • Your View's xaml can then be declared like:

Example:

...
<Window.Resources>
  <DataTemplate DataType="{x:Type local:Instance1ViewModel}">
    <local:Instance1View />
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:Instance2ViewModel}">
    <local:Instance2View />
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:Instance3ViewModel}">
    <local:Instance3View />
  </DataTemplate>
</Window.Resources>
<Grid>
  <ContentControl Content="{Binding Path=CurrentFrame}" />
</Grid>
...
  • Thats it!. Now you just switch the CurrentFrame property in your view-model and make it point to any of three instance view-models and the view will be correspondingly updated.

This gets you an MVVM compliant application, for your other issue of working around not having to recreate views dynamically based on DataTemplate you could follow the approaches suggested here and expand it for your own usage.

Community
  • 1
  • 1
Viv
  • 17,170
  • 4
  • 51
  • 71
  • Thanks for a thorough answer! I understand all this, and that was my first attempt at fixing it - using the View as a DataTemplate for the ViewModel. However, in the View, I bind VisualStates to ViewModel-properties, and when I am switching the ViewModel bound to my ContentControl to another VM, and then switching it back - I seem to have two View-instances in memory, both of them having their ViewState bound to my ViewModel. Each time I re-bind the ContentControl, it creates a new instance of the View. – cederlof Mar 29 '13 at 20:24
  • I can't find a solution where I would get the _same_ View-instance used inside the ContentControl. Thats when I tried to bind the View-instance to the ContentControl, instead of the ViewModel - and so - breaking the MVVM-pattern. – cederlof Mar 29 '13 at 20:25
  • @cederlof No problem. I get your issue with preserving views while switching them from the view model. Did you have a look at the link at the very last para of my post. That's a question exactly like yours with some helpful answers. You could try Rachael's example and apply it to your container to get your requirement and not break MVVM principles. Give that a try if you haven't as it seems to address the issue efficiently – Viv Mar 29 '13 at 20:35
  • Thanks! However those solutions do not seem to work for me as I would like to use the same View-instance inside another ContentPresenter-control also, moving it from one ContentPresenter to another. Sorry my question is not entirely clear... – cederlof Apr 08 '13 at 09:07